Python中的原子状态存储?

2024-03-28 14:44:55 发布

您现在位置:Python中文网/ 问答频道 /正文

我在做一个不可靠系统的项目,我认为它在任何时候都可能失败。我要保证的是,如果我写了_状态,而机器在操作中失败,则read_状态将读取有效状态或根本没有状态。我已经实现了一些我认为会在下面起作用的东西——我对那些批评或者其他解决方案感兴趣,如果有人知道的话。在

我的想法是:

import hashlib, cPickle, os

def write_state(logname, state):
    state_string = cPickle.dumps(state, cPickle.HIGHEST_PROTOCOL)
    state_string += hashlib.sha224(state_string).hexdigest()

    handle = open('%s.1' % logname, 'wb')
    handle.write(state_string)
    handle.close()

    handle = open('%s.2' % logname, 'wb')
    handle.write(state_string)
    handle.close()

def get_state(logname):
    def read_file(name):
        try:
            f = open(name,'rb')
            data = f.read()
            f.close()
            return data
        except IOError:
            return ''
    def parse(data):
        if len(data) < 56:
            return (None, '', False)
        hash = data[-56:]
        data = data[:-56]
        valid = hashlib.sha224(data).hexdigest() == hash
        try:
            parsed = cPickle.loads(data)
        except cPickle.UnpicklingError:
            parsed = None
        return (parsed, valid)

    data1,valid1 = parse(read_file('%s.1'%logname))
    data2,valid2 = parse(read_file('%s.2'%logname))

    if valid1 and valid2:
        return data1
    elif valid1 and not valid2:
        return data1
    elif valid2 and not valid1:
        return data2
    elif not valid1 and not valid2:
        raise Exception('Theoretically, this never happens...')

例如:

^{pr2}$

Tags: andreaddatastringreturn状态defnot
3条回答

我对数据库工作方式的模糊记忆是这样的。它包括三个文件。控制文件、目标数据库文件和挂起的事务日志。在

控制文件具有全局事务计数器和哈希或其他校验和。这是一个只有一个物理块大小的小文件。一个操作系统级写入。在

在目标文件中有一个全局事务计数器,其中包含实际数据,外加哈希或其他校验和。在

有一个挂起的事务日志刚刚增长,或者是一个有限大小的循环队列,或者可能会滚动。没什么大不了的。在

  1. 将所有挂起的事务记录到简单日志中。有序列号和变化的内容。

  2. 更新事务计数器,更新控制文件中的哈希。一封信,满脸通红。如果这失败了,那么一切都没有改变。如果成功,则控制文件和目标文件不匹配,表示事务已启动但尚未完成。

  3. 对目标文件执行预期的更新。查找开头并更新计数器和校验和。如果失败,则控制文件的计数器比目标文件多。目标文件已损坏。这样做时,最后记录的事务、控制文件和目标文件都同意序列号。

您可以通过重放日志来恢复,因为您知道最后一个正确的序列号。在

你的两份不起作用。文件系统可以重新排序,这样两个文件在写入磁盘之前都被截断了。在

有一些文件系统操作被保证是原子的:在另一个文件上重命名文件是一种操作,只要文件在一个地方或另一个地方。然而,就POSIX而言,它不能保证在文件内容到达磁盘之前就完成了移动,这意味着它只给您提供锁定。在

Linux文件系统已经强制要求文件内容在原子移动之前到达磁盘(但不是同步的),所以这可以满足您的需要。ext4在短时间内打破了这一假设,使得那些文件实际上更有可能变成空文件。这是widely regarded as a dick move,自那时起已被纠正。在

无论如何,正确的方法是:在同一个目录中创建临时文件(因此它在同一个文件系统中);写入新数据;fsync临时文件;将其重命名为以前的版本。这是操作系统所能保证的原子级的。它还以盘旋磁盘为代价提供了耐用性,这就是为什么应用程序开发人员不喜欢使用fsync,也不想把有问题的ext4版本列入黑名单。在

我将添加一个异端的回答:使用sqlite怎么样?或者,可能是bsddb,但是这似乎是不推荐使用的,您必须使用第三方模块。在

相关问题 更多 >