是否可以在 EFS 中可靠地使用 SQLite?
Is it possible to use SQLite in EFS reliably?
是否可以安全地在 AWS EFS 中使用 SQLite?在我试图确定这是否可行的读数中,似乎有一些暗示它应该可行,因为 AWS EFS 在 2017 年实施了 NFSv4。实际上,我没有运气从中获得一致的行为。
快速积分:
- “只需使用 AWS RDS”:由于其他 AWS 架构存在问题,另一个团队已实施我们正在尝试解决由 API(DynamoDB 不是一个选项)[=34 导致的资源匮乏问题=]
- “这违背了 SQLite 的主要用例(作为本地访问数据库):是的,但考虑到这种情况,这似乎是最好的方法。
- 我已验证我们在 EC2 实例上 运行 nfsv4
无论我使用何种方法,当前结果都与遇到的 3 个异常非常不一致
- “文件已加密或不是数据库”
- “磁盘 I/O 错误(可能与 EFS 打开文件限制有关)”
- “数据库磁盘映像格式错误”(此后数据库实际上没有损坏)
数据库代码:
SQLITE_VAR_LIMIT = 999
dgm_db_file_name = ''
db = SqliteExtDatabase(None)
lock_file = f'{os.getenv("efs_path", "tmp")}/db_lock_file.lock'
def lock_db_file():
with open(lock_file, 'w+') as lock:
limit = 900
while limit:
try:
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
print(f'db locked')
break
except Exception as e:
print(f'Exception: {str(e)}')
limit -= 1
time.sleep(1)
if not limit:
raise ValueError(f'Timed out after 900 seconds while waiting for database lock.')
def unlock_db_file():
with open(lock_file, 'w+') as lock:
fcntl.flock(lock, fcntl.LOCK_UN)
print(f'db unlocked')
def initialize_db(db_file_path=dgm_db_file_name):
print(f'Initializing db ')
global db
db.init(db_file_path, pragmas={
'journal_mode': 'wal',
'cache_size': -1 * 64000, # 64MB
'foreign_keys': 1})
print(f'db initialized')
class Thing(Model):
name = CharField(primary_key=True)
etag = CharField()
last_modified = CharField()
class Meta:
database = db
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@staticmethod
def insert_many(stuff):
data = [(k, v['ETag'], v['Last-Modified']) for k, v in stuff.items()]
fields = [Thing.name, Thing.etag, Thing.last_modified]
limit = 900
while True:
try:
with db.atomic():
for key_batch in chunked(data, SQLITE_VAR_LIMIT // len(fields)):
s = Thing.insert_many(key_batch, fields=[Thing.name, Thing.etag, Thing.last_modified]) \
.on_conflict_replace().execute()
break
except Exception as e:
print(f'Exception: {str(e)}')
print(f'Will try for {limit} more seconds.')
limit -= 1
time.sleep(1)
if not limit:
raise ValueError('Failed to exectue query after 900 seconds.')
调用示例:
print(f'Critical section start')
# lock_db_file() # I have tried with a secondary lock file as well
self.stuff_db = Thing()
if not Path(self.db_file_path).exists():
initialize_db(self.db_file_path)
print('creating tables')
db.create_tables([Thing], safe=True)
else:
initialize_db(self.db_file_path)
getattr(Thing, insert_many)(self.stuff_db, stuff_db)
# db.close()
# unlock_db_file()
print(f'Critical section end')
print(f'len after update: {len(stuff)}')
其他特点:
- 如果一个 lamda 卡住捕获“格式错误的图像”异常并触发新的 lambda 执行,则错误在另一个 lambda 中解决。
经过反复试验,我发现这是一个可行的解决方案。看来设计需要使用 APSWDatabase(..., vfs='unix-excl') 来正确执行锁定。
数据库代码:
from peewee import *
from playhouse.apsw_ext import APSWDatabase
SQLITE_VAR_LIMIT = 999
db = APSWDatabase(None, vfs='unix-excl')
def initialize_db(db_file_path):
global db
db.init(db_file_path, pragmas={
'journal_mode': 'wal',
'cache_size': -1 * 64000})
db.create_tables([Thing], safe=True)
return Thing()
class Thing(Model):
field_1 = CharField(primary_key=True)
field_2 = CharField()
field_3 = CharField()
class Meta:
database = db
这允许以下用法:
db_model = initialize_db(db_file_path)
with db:
# Do database queries here with the db_model
pass
注意:如果您不使用上下文管理的数据库连接,则需要显式调用 db.close()
,否则不会从文件中释放锁。此外,调用 db_init(...)
会导致在数据库关闭之前锁定它。
是否可以安全地在 AWS EFS 中使用 SQLite?在我试图确定这是否可行的读数中,似乎有一些暗示它应该可行,因为 AWS EFS 在 2017 年实施了 NFSv4。实际上,我没有运气从中获得一致的行为。
快速积分:
- “只需使用 AWS RDS”:由于其他 AWS 架构存在问题,另一个团队已实施我们正在尝试解决由 API(DynamoDB 不是一个选项)[=34 导致的资源匮乏问题=]
- “这违背了 SQLite 的主要用例(作为本地访问数据库):是的,但考虑到这种情况,这似乎是最好的方法。
- 我已验证我们在 EC2 实例上 运行 nfsv4
无论我使用何种方法,当前结果都与遇到的 3 个异常非常不一致
- “文件已加密或不是数据库”
- “磁盘 I/O 错误(可能与 EFS 打开文件限制有关)”
- “数据库磁盘映像格式错误”(此后数据库实际上没有损坏)
数据库代码:
SQLITE_VAR_LIMIT = 999
dgm_db_file_name = ''
db = SqliteExtDatabase(None)
lock_file = f'{os.getenv("efs_path", "tmp")}/db_lock_file.lock'
def lock_db_file():
with open(lock_file, 'w+') as lock:
limit = 900
while limit:
try:
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
print(f'db locked')
break
except Exception as e:
print(f'Exception: {str(e)}')
limit -= 1
time.sleep(1)
if not limit:
raise ValueError(f'Timed out after 900 seconds while waiting for database lock.')
def unlock_db_file():
with open(lock_file, 'w+') as lock:
fcntl.flock(lock, fcntl.LOCK_UN)
print(f'db unlocked')
def initialize_db(db_file_path=dgm_db_file_name):
print(f'Initializing db ')
global db
db.init(db_file_path, pragmas={
'journal_mode': 'wal',
'cache_size': -1 * 64000, # 64MB
'foreign_keys': 1})
print(f'db initialized')
class Thing(Model):
name = CharField(primary_key=True)
etag = CharField()
last_modified = CharField()
class Meta:
database = db
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@staticmethod
def insert_many(stuff):
data = [(k, v['ETag'], v['Last-Modified']) for k, v in stuff.items()]
fields = [Thing.name, Thing.etag, Thing.last_modified]
limit = 900
while True:
try:
with db.atomic():
for key_batch in chunked(data, SQLITE_VAR_LIMIT // len(fields)):
s = Thing.insert_many(key_batch, fields=[Thing.name, Thing.etag, Thing.last_modified]) \
.on_conflict_replace().execute()
break
except Exception as e:
print(f'Exception: {str(e)}')
print(f'Will try for {limit} more seconds.')
limit -= 1
time.sleep(1)
if not limit:
raise ValueError('Failed to exectue query after 900 seconds.')
调用示例:
print(f'Critical section start')
# lock_db_file() # I have tried with a secondary lock file as well
self.stuff_db = Thing()
if not Path(self.db_file_path).exists():
initialize_db(self.db_file_path)
print('creating tables')
db.create_tables([Thing], safe=True)
else:
initialize_db(self.db_file_path)
getattr(Thing, insert_many)(self.stuff_db, stuff_db)
# db.close()
# unlock_db_file()
print(f'Critical section end')
print(f'len after update: {len(stuff)}')
其他特点:
- 如果一个 lamda 卡住捕获“格式错误的图像”异常并触发新的 lambda 执行,则错误在另一个 lambda 中解决。
经过反复试验,我发现这是一个可行的解决方案。看来设计需要使用 APSWDatabase(..., vfs='unix-excl') 来正确执行锁定。
数据库代码:
from peewee import *
from playhouse.apsw_ext import APSWDatabase
SQLITE_VAR_LIMIT = 999
db = APSWDatabase(None, vfs='unix-excl')
def initialize_db(db_file_path):
global db
db.init(db_file_path, pragmas={
'journal_mode': 'wal',
'cache_size': -1 * 64000})
db.create_tables([Thing], safe=True)
return Thing()
class Thing(Model):
field_1 = CharField(primary_key=True)
field_2 = CharField()
field_3 = CharField()
class Meta:
database = db
这允许以下用法:
db_model = initialize_db(db_file_path)
with db:
# Do database queries here with the db_model
pass
注意:如果您不使用上下文管理的数据库连接,则需要显式调用 db.close()
,否则不会从文件中释放锁。此外,调用 db_init(...)
会导致在数据库关闭之前锁定它。