Transactions - laforge49/JFile GitHub Wiki

Transactions are single threaded, which is to say they are executed one at a time. This means that all the interactions between a transaction and the database can be done using simple method calls.

There is no rollback. Two-stage commit is not supported. Transactions then must be written and used with care, as an exception will cause the database to crash.

Transactions are aggregated into blocks for faster logging, and processed only after the block has been fsync'd. And responses from transactions are returned only after the entire block has been processed. So if there is a failure and the last block of transactions is dropped for some reason, no responses will have been sent from those dropped transactions.

##Transactions with Persistent State

On the previous page, DB, we looked at two transactions which subclass _TransactionJid, and which had no persistent state. Now we will look at transactions which subclass _StringTransactionJid and _TupleTransactionJid, which have a persistent String and tuple respectively.

public class GetIntegerTransaction extends _StringTransactionJid {
    public static byte[] bytes(Mailbox mailbox, String key)
            throws Exception {
        GetIntegerTransaction git = new GetIntegerTransaction(mailbox, key);
        return git.getBytes();
    }

    public GetIntegerTransaction() {
    }

    public GetIntegerTransaction(Mailbox mailbox, String key)
            throws Exception {
        initialize(mailbox);
        setValue(key);
    }

    protected void eval(long blockTimestamp, RP rp) throws Exception {
        IMDB imdb = (IMDB) getAncestor(IMDB.class);
        rp.processResponse(imdb.getInteger(getValue()));
    }
}

The GetIntegerTransaction, like the earlier transactions which have no state, has an eval method which is called to execute the transaction. The transaction access the database (IMDB) as an ancestor, which is to say that the database is included in the dependency stack.

There are two constructors. The default constructor is used when the transaction is deserialized or cloned. The second constructor is useful when initially creating the transaction with a value.

The static bytes method is also a convenience method for creating the transaction with a value and then serializing the transaction. This method can be used by static methods in the transaction's factory.

public class GetIntegerTransactionFactory extends ActorFactory {
    final public static GetIntegerTransactionFactory fac = new GetIntegerTransactionFactory();

    public static AggregateTransaction at(Mailbox mailbox, String key)
            throws Exception {
        byte[] gitBytes = GetIntegerTransaction.bytes(mailbox, key);
        return new AggregateTransaction(fac, gitBytes);
    }

    public GetIntegerTransactionFactory() {
        super(JFileFactories.GET_INTEGER_TRANSACTION);
    }

    protected JLPCActor instantiateActor() throws Exception {
        return new GetIntegerTransaction();
    }
}

The transaction factory for GetIntegerTransaction is very much the same as other factories, except for the static at method, which returns an AggregateTransacton that will execute the GetIntegerTransaction with the provided string.

Transactions often require a richer persistent state than just a string. Fortunately the added complexity is minimal. A good example is the AddIntegerTransaction, which requires both a String key and an Integer increment:

public class AddIntegerTransaction extends _TupleTransactionJid {
    private static ActorFactory afs[] = {StringJidFactory.fac, IntegerJidFactory.fac};

    public static byte[] bytes(Mailbox mailbox, String key, Integer increment)
            throws Exception {
        AddIntegerTransaction ait = new AddIntegerTransaction(mailbox, key, increment);
        return ait.getBytes();
    }

    public AddIntegerTransaction() {}

    public AddIntegerTransaction(Mailbox mailbox, String key, Integer increment)
            throws Exception {
        initialize(mailbox);
        getKeyJid().setValue(key);
        getIncrementJid().setValue(increment);
    }

    protected ActorFactory[] getTupleFactories() throws Exception {
        return afs;
    }

    protected StringJid getKeyJid()
            throws Exception {
        return (StringJid) iGet(0);
    }

    protected IntegerJid getIncrementJid()
            throws Exception {
        return (IntegerJid) iGet(1);
    }

    protected void eval(long blockTimestamp, RP rp) throws Exception {
        IMDB imdb = (IMDB) getAncestor(IMDB.class);
        Integer nv = imdb.addInteger(getKeyJid().getValue(), getIncrementJid().getValue());
        rp.processResponse(nv);
    }
}

The requirement when subclassing _TupleTransactionJid is to supply an array of the JID factories used to instantiate the elements of the persistent tuple. As shown above, an easy way to do this is to override the getTupleFactories method.

##JFile Classes Referenced on this Page

IMDB
GetIntegerTransaction
GetIntegerTransactionFactory
AddIntegerTransaction

Back: DB Up: Home Next: IMDB