A write to the datastore is more involved than just storing an object into Cassandra (we use Cassandra for this document but is interchangeable with other BigTable technologies such as Hypertable and HBase). Additional steps must take place for query functionality and transaction semantics. This article explains the procedures that take place every time an application does a put() call within an application.
The programming model within AppScale consists of entity groups, where entities are programmatically assigned to groups. By default all entities are in their own entity group but newly created entities can specify parent entities. Once a relationship is established, updates happen atomically to entity groups. Locks are acquired to prevent concurrent writes to the same entity group.
If the lock was acquired from ZooKeeper, a new transaction ID is attained from ZooKeeper as well. A fetch in Cassandra is done on the entity key to see if the entity existed before. The entity is stored along side with its transaction version. If the entity did exist before the transaction version is registered with ZooKeeper to know what version to rollback to if the current on-going transaction fails.
If a previous version of the entity existed, the indexes associated with that entity are deleted from three different tables: Ascending, Descending, and the Kind table. The tables enable the ability to scalably query for entities based on their type or properties, where each index is a reference to a key in the Entity Table. The Entity Table stores entities with the row key built based on an entity's group allowing for ancestor queries. We have a descending table where the property values are lexicographically reversed because BigTable only allows scans in ascending order. The Kind Table has the row key flip the ancestry order to allow for kind queries.
After old indexes are deleted, the serialized entity is stored in the Entity table and the Journal table (the journal key is the entity key with the transaction ID appended). New indexes are updated upon successfully updating those tables. Entity and Journal updates are done in parallel, as are the index updates to minimize latency.
The lock for the entity group is released after the previous steps have successfully finished. Meta data from ZooKeeper is cleaned up in this step.