原子性:实现资源管理器
Atomicity: Implementing a Resource Manager
我正在尝试理解以下文档:
我发现该文档难以阅读和理解,尤其是它给人一种错误的感觉,即在应用程序级别支持事务。让我们考虑以下 SO 参考,其中发布者请求在事务中支持 File.Move
:
- How to write a transaction to cover Moving a file and Inserting record in database?
截至今天,接受的答案是(截断):
TxFileManager fileMgr = new TxFileManager();
using (TransactionScope scope1 = new TransactionScope())
{
fileMgr.Copy(srcFileName, destFileName);
scope1.Complete();
}
首先突出的是接受的答案稍微修改了原始发布者的请求,在该答案中更改了行:
fileMgr.Move(srcFileName, destFileName);
进入:
fileMgr.Copy(srcFileName, destFileName);
重要的一点是底层系统(实际文件系统)可能不提供原子 File.Move
操作(例如,显然在跨越文件系统边界时)。
我应该如何阅读上述文档以了解实施资源管理器时的实际要求是什么?特别是,当底层系统不提供 true 原子操作时,是否可以实现资源管理器(我们可以以 File.Move
跨文件系统边界的操作为例例如)?
首先,我认为你应该让你的资源管理器持久化:
然后你可以在 Prepare
方法(两阶段提交协议的第一阶段)中将你的文件移动操作保存到持久存储(文件,数据库 table,..)像 (Operation:FileMove, SrcFile, DesFile)
这样的格式,这也有助于稍后在进程崩溃时进行恢复:
https://docs.microsoft.com/en-us/dotnet/api/system.transactions.preparingenlistment?view=net-5.0 .
public void Prepare(PreparingEnlistment preparingEnlistment)
{
Console.WriteLine("Prepare notification received");
//Validate if the File.Move is valid if it's executed later, then save your file moving operation to a temporary durable storage
//If work finished correctly, reply prepared
preparingEnlistment.Prepared();
// otherwise, do a ForceRollback
preparingEnlistment.ForceRollback();
}
在阶段 2(Commit
),您可以从持久存储加载操作以实际移动文件(使用 重试 以确保 true
原子操作)
public void Commit(Enlistment enlistment)
{
Console.WriteLine("Commit notification received");
//Read to file moving operation from phase 1 to move the file and retry if the failure is transient
//Declare done on the enlistment when the files have been moved
enlistment.Done();
}
第一阶段保存文件移动操作的要点是确保:
- 如果在
Commit
阶段发生任何故障(由于 File.Move
跨文件系统边界操作)=> enlistment.Done()
是 而不是 调用所以 Commit
方法可以稍后执行,我们仍然有阶段 1 中的信息可以重试。
- 因为我们的资源管理器是持久的,如果进程在
Commit
方法中崩溃,在恢复过程中我们可以重试文件移动操作,这要归功于 Prepare
阶段[=中保留的信息43=]
我正在尝试理解以下文档:
我发现该文档难以阅读和理解,尤其是它给人一种错误的感觉,即在应用程序级别支持事务。让我们考虑以下 SO 参考,其中发布者请求在事务中支持 File.Move
:
- How to write a transaction to cover Moving a file and Inserting record in database?
截至今天,接受的答案是(截断):
TxFileManager fileMgr = new TxFileManager();
using (TransactionScope scope1 = new TransactionScope())
{
fileMgr.Copy(srcFileName, destFileName);
scope1.Complete();
}
首先突出的是接受的答案稍微修改了原始发布者的请求,在该答案中更改了行:
fileMgr.Move(srcFileName, destFileName);
进入:
fileMgr.Copy(srcFileName, destFileName);
重要的一点是底层系统(实际文件系统)可能不提供原子 File.Move
操作(例如,显然在跨越文件系统边界时)。
我应该如何阅读上述文档以了解实施资源管理器时的实际要求是什么?特别是,当底层系统不提供 true 原子操作时,是否可以实现资源管理器(我们可以以 File.Move
跨文件系统边界的操作为例例如)?
首先,我认为你应该让你的资源管理器持久化:
然后你可以在 Prepare
方法(两阶段提交协议的第一阶段)中将你的文件移动操作保存到持久存储(文件,数据库 table,..)像 (Operation:FileMove, SrcFile, DesFile)
这样的格式,这也有助于稍后在进程崩溃时进行恢复:
https://docs.microsoft.com/en-us/dotnet/api/system.transactions.preparingenlistment?view=net-5.0 .
public void Prepare(PreparingEnlistment preparingEnlistment)
{
Console.WriteLine("Prepare notification received");
//Validate if the File.Move is valid if it's executed later, then save your file moving operation to a temporary durable storage
//If work finished correctly, reply prepared
preparingEnlistment.Prepared();
// otherwise, do a ForceRollback
preparingEnlistment.ForceRollback();
}
在阶段 2(Commit
),您可以从持久存储加载操作以实际移动文件(使用 重试 以确保 true
原子操作)
public void Commit(Enlistment enlistment)
{
Console.WriteLine("Commit notification received");
//Read to file moving operation from phase 1 to move the file and retry if the failure is transient
//Declare done on the enlistment when the files have been moved
enlistment.Done();
}
第一阶段保存文件移动操作的要点是确保:
- 如果在
Commit
阶段发生任何故障(由于File.Move
跨文件系统边界操作)=>enlistment.Done()
是 而不是 调用所以Commit
方法可以稍后执行,我们仍然有阶段 1 中的信息可以重试。 - 因为我们的资源管理器是持久的,如果进程在
Commit
方法中崩溃,在恢复过程中我们可以重试文件移动操作,这要归功于Prepare
阶段[=中保留的信息43=]