python-3.x - 是否可以在 EFS 中可靠地使用 SQLite?

标签 python-3.x amazon-web-services sqlite peewee amazon-efs

是否可以在 AWS EFS 中安全地使用 SQLite?在我试图确定这是否可行的阅读 Material 中,似乎有一些暗示它应该可行,因为 AWS EFS 在 2017 年实现了 NFSv4。实际上,我没有运气从中获得一致的行为。

快点:

  1. “只需使用 AWS RDS”:由于其他 AWS 架构存在问题,另一个团队已实现我们正在尝试解决由 API 导致的资源匮乏问题(DynamoDB 不是一个选项)
  2. “这与 SQLite 的主要用例(作为本地访问数据库)背道而驰:是的,但考虑到这种情况,这似乎是最好的方法。
  3. 我已验证我们正在 EC2 实例上运行 nfsv4

无论我使用何种方法,当前结果都与遇到的 3 个异常非常不一致

  1. “文件已加密或不是数据库”
  2. “磁盘 I/O 错误(可能与 EFS 打开文件限制有关)”
  3. “database disk image is malformed”(此后数据库实际上并没有损坏)

数据库代码:

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(...) 会在 databased 上放置一个锁,直到它关闭。

关于python-3.x - 是否可以在 EFS 中可靠地使用 SQLite?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71042228/

相关文章:

amazon-web-services - Amazon Intrinsic 函数可从一个帐户获取所有 VPC 以进行云形成

database - wxSQLite3 中准备语句重用的异常

android - 查询 "cont"列上最大值的行

java - 这是在 Java 中释放 SQLite 连接的最佳方法吗?

python-3.x - python3.6安装uwsgi报错centos

python - 如何在Python循环中从所有返回中获得最终结论?

reactjs - Graphql apollo 客户端从 AppSync 返回空值

django - 使用 django-rest-framework 处理 django 中多个应用程序的权限

python - 如何在python3中使用selenium和phantomjs进行网页截图之前等待?

amazon-web-services - 将 AWS EC2 上的 Nginx 的高性能 Web 服务转换为 AWS Lambda