Question

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.

Was it helpful?

Solution

I haven't found anything similar in PHP, so I started my own php-transactions project to fill the gap.

It will have implementations of distributed transaction and filesystem transaction with limited functionality. I intend to add some support for cooperative multitasking, with that it will be easier to separate filesystem and database transactions when they depend on each other. It will work with promises, and generators as well. I think I'll finish with the 1.0 until the end of June 2014.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top