Monday, March 17, 2008

Xtensive.Indexing architecture: logical view, part 2

4. DifferentialIndex

That's what we're using to describe index changes. Differential index is noting more then a wrapper over regular index adding "+" (added), "-" (removed) and "*" (changed) marks against each of its items (they aren't included into the key, of course). Any differential index knows the slice with its own changes, as well as the DifferentialIndex below it (the index to which changes are applied).

As I've mentioned, we can't change the indexes on disk - once some index (i.e. a slice of differential index) is persisted, it can only be read, and finally - disposed by index garbage collector.

5. TransactionalIndexSet and TransactionalIndex

TransactionalIndexSet manages index representation for a particular transaction. It allows to:
- Create a TransactionalIndex for specified Transaction - the changeable index view (TransactionalIndex) you'll get will look exactly like a snapshot isolated index for this transaction should look (i.e. no any changes made after its start will be visible)
- Manage index slice sequence - the chain of the slices forming the index. It provides any part of this chain (from T1 to T2) and supports two chain modify operations: push a new slice on the top of it (that's what happens on transaction commit) and replace two old slices by a new one (that's what background index merge process does). Its garbage collector removes the useless slices (e.g. as the result of merge or rollback), but any slice exists at least for the duration of chain cache period (~ 1 min.) after the time it was actually released from the TransactionalIndexSet chain sequence - this allows TransactionalIndexes to get chain updates much rarely.
- Handle Transaction.Commits and Rollbacks - by detecting the conflicts on commits and pushing the committed slice into to common chain sequence and releasing the newly created slices (differential indexes) on rollbacks.
- Handle recovery after the failure.

Finally, it logs everything into the TransactionLog it is bound to (redo log), ensures it is flushed on commits and "resurrects" the index state after the last successful commit on recovery.

TransactionalIndex is actually rather simple wrapper over DifferentialIndex - it pushes all the actions made to it into TransactionLog and takes care about the size \ location of the underlying index slice(s). The topmost slice it works with is always in-memory index; but when in reaches a certain limit, it creates a new slice, and serializes the old one to disk in background (it's safe, since old one is already a read-only slice at this point).

6. NonUniqueIndex

It is a wrapper transforming some unique index into a non-unique one - by rejecting a part of its key.

Note: any relational index is internally described as unique index: if it is primary index, then its key is unique itself; if it is secondary index, it can be exposed as an index with the following key: (NonUniqueKey, ForeignKey). Since ForeignKey is unqiue (as PrimaryKey), the whole key is unique. So NonUnqiueIndex just "strips" the ForeignKey part of the key, and exposes it as non-unique index with just NonUnqiueKey key.

Physical view: to be continued...

No comments:

Post a Comment