I am looking for a filesystem library which implements the command pattern, so I can undo changes made in the filesystem, and for example by database errors I can protect the data integrity easily in a primitive way (with probably pessimistic locking)...
Do you know about such a system, or do I have to implement it myself?
This example will be a little bit dummy, because we usually would do it with file replacement and we would keep the file name in the database. Please forget about that for a moment...
Example:
try {
$delete = $file1->delete();
$create = $file2->create();
$trans = $db->createTransaction(function () use ($db){
$stmt = $db->prepare('UPDATE files SET name = :new_name WHERE name = :old_name');
$stmt->execute(array(
'old_name' => $file1->name,
'new_name' => $file2->name
));
});
$trans->commit();
}
catch(Exception $e){
if ($trans)
$trans->rollback();
if ($create)
$create->undo();
if ($delete)
$delete->undo();
throw $e;
}
Ofc. I want to handle the rollback automatically and check whether the file locking order (if there is a lock) is abc to prevent deadlocks.
For example with an interface something like this:
try {
$commands = new Commands();
$commands->add(new FileDeleteCommand($file1));
$commands->add(new FileCreateCommand($file2));
$commands->add(new DatabaseTransaction(
function ($db) use ($file1, $file2){
$stmt = $db->prepare('UPDATE files SET name = :new_name WHERE name = :old_name');
$stmt->execute(array(
'old_name' => $file1->name,
'new_name' => $file2->name
));
}
));
$commands->execute();
}
catch(Exception $e){
$commands->undo();
throw $e;
}
There are limitations by this approach, for example I have to commit the transaction in the last place, because I cannot rollback it when it is already committed, but I can live with that...
Or an alternative interface:
try {
$transactions = new DistributedTransaction();
$transactions->add(new FileSystemTransaction(
function () use ($file1, $file2){
$file1->delete();
$file2->create();
}
));
$transactions->add(new DatabaseTransaction(
function ($db) use ($file1, $file2){
$stmt = $db->prepare('UPDATE files SET name = :new_name WHERE name = :old_name');
$stmt->execute(array(
'old_name' => $file1->name,
'new_name' => $file2->name
));
}
));
$transactions->commit();
}
catch(Exception $e){
$transactions->rollback();
throw $e;
}
Ofc the problem is that how do we commit 2 transactions at once? I think the only way that we implement one of them: the filesystem transaction, but I am open for suggestions...
I added a new tag, I think this is a simple distributed transaction issue with a database transaction resource and a filesystem transaction resource.