过去,作为一种廉价的处理方式,我使用了重命名来
移动文件或将其标记为正在处理。如果重命名
成功了,那么进程就知道它“拥有”该文件并且会
继续处理。如果它失败是因为源文件没有
存在,那么它会尝试目录中的下一个文件。
首先让我指出,这种使用原子重命名线程来获取独占访问权以处理文件的技术是可行的,但它确实存在使文件未处理的风险。想象一下如果线程(或整个服务器)在重命名后立即死亡会发生什么。如果没有可靠的方法来跟踪哪些文件尚未完成并重试它们,您的系统将不会很有弹性。
正如您所注意到的,S3 没有原子重命名操作,因此您常用的技术无法按您的意愿工作。
S3 有一个很好的“通知”功能,可以配置。在您的情况下,您可能希望在创建文件时收到通知。通知可以传送到 SNS、SQS 或 Lambda。您可能需要 SQS 或 Lambda。使用 SQS,消息被添加到队列中,您可以让线程抓取并处理文件。 SQS 模型保证“至少一次”传递,并将重试传递,直到消息被删除(或超出队列时间)。 redeliver-if-not-deleted 时间是可配置的。请注意,SQS 有可能多次传递相同的消息 - 它们在过度传递而不是不传递消息方面犯了错误。如果可以不频繁地对文件进行双重处理,那么这对您来说可能很好。我们广泛使用了 SQS 队列并且很高兴。
我不熟悉 Lambda 消息处理的详细语义。
我建议您谷歌“S3 事件通知”了解更多详情。
原始问题的原始答案:
我不确定问题是“线程安全”——也许更多的是“事务完整性”?
在任何情况下,您都认为进行 S3“原子”重命名并不明显是正确的。我认为你必须“挑选你的毒药”——要么你必须处理这样一个事实:1)你同时拥有旧的和新的副本,或者 2)你有一段时间既没有旧的也没有旧的。新副本。
在任何一种情况下,您需要处理的一个关键问题是坚持您正在进行重命名的事实(直到确认重命名完成)。如果您在某个数据库中有一行代表该文件,那么您可以将状态保留在那里。以下假设您不想使用 S3 以外的任何东西来保持状态。
您将实际复制文件两次,使用临时文件夹作为中间副本。您可以让单独的线程执行每个步骤(查找要处理的文件),或者使用单个线程检查各种条件并执行其余步骤。换句话说,您需要查找部分完成的重命名(但该线程未能完成)并从中断处继续。
对于本例,我们将从 A 重命名为 B,并使用一个名为 tmp 的临时文件夹。
如果您更喜欢同时拥有两个副本:
1. Copy A to tmp/A-B (the file name has before and after names in it).
2. Finding tmp/A-B: copy it to B.
3. Finding tmp/A-B, A and B: delete A.
4. Finding tmp/A-B, A is missing and B exists: delete tmp/A-B.
如果您更喜欢暂时没有副本:
1. Copy A to tmp/A-B.
2. Finding tmp/A-B and A: delete A.
3. Finding tmp/A-B and A is missing and B is missing: copy tmp/A-B to B.
4. Finding tmp/A-B and A is missing and B exists: delete tmp/A-B.