IndexCatalog

class IndexCatalog(val directory: Path, val options: IndexOptions = IndexOptions.DEFAULT) : SegmentObserver, AutoCloseable

The indexes a store has, and the sidecars that carry them.

val indexes = IndexCatalog(directory)
DocumentStore.open(directory, StoreOptions(segmentObserver = indexes)).use { store ->
indexes.attach(store)
val handle = indexes.createIndex(store, IndexDefinition.inverted("$.team"))
store.snapshot().use { snapshot ->
indexes.read(store, handle, snapshot).use { reader ->
println(IndexQuery.keysEqualTo(store, reader, IndexTerm.ofString("analytics")))
}
}
}

An index is per-segment immutable sidecar files, and that single constraint produces everything this class does. Creating one over existing data writes new files against segments that are already written, so no document is rewritten and createIndex over ten million documents costs a scan rather than a rebuild. A query uses the sidecars that exist and scans where they do not, so an index is usable while it is still building, with no cutover. dropIndex deletes files. And a compaction replaces the sidecars of the segments it consumed with sidecars for the segments it produced, as a consequence of the merge rather than as a step anybody has to remember.

There is no compaction-time merge of posting lists, and that is a decision rather than a gap. Ordinals are positions within a segment, so a compaction renumbers every one of them; merging two input posting lists would mean remapping every ordinal in both. Reading the term back out of the document — which the compaction is already holding, already decoded, on a pass it was making anyway — is strictly less work than that. So compaction awareness here is structural: each output segment opens its own observation, and retain prunes the inputs.

Two objects and the order matters, as it does for the catalog — and here it is three. The observer has to be installed in StoreOptions before the store opens, because a flush can begin the moment it does; attach loads the sidecars and covers whatever they do not; and close must be called, because nothing else releases the mappings and on Windows a mapped file cannot be deleted at all. That is honest and it is not ergonomic. Smoothing it over is rabosh-api's job.

Thread safety. Safe from any thread. The store's maintenance thread drives beginSegment, complete and retain while a reader may be inside read; one segment's accumulation happens on one thread and is not shared. Since phase 15 this catalog has a thread of its own as well — see createIndexInBackground — which drives exactly the same three callbacks and is subject to exactly the same rules.

Constructors

Link copied to clipboard
constructor(directory: Path, options: IndexOptions = IndexOptions.DEFAULT)

Properties

Link copied to clipboard

The store directory sidecars live in. The same directory the store was opened on.

Link copied to clipboard

Whether attach has been called. Nothing is read before it has.

Link copied to clipboard
Link copied to clipboard

Failures raised inside this catalog's own callbacks, in order.

Functions

Link copied to clipboard
fun attach(store: DocumentStore, backfill: Boolean = true)

Loads the registry and the sidecars, builds whatever is missing, and starts maintaining them.

Link copied to clipboard
open override fun beginSegment(segmentNumber: Long): SegmentObservation?
Link copied to clipboard

Builds sidecars for whatever is not yet covered, on the catalog's own thread, returning at once.

Link copied to clipboard
open override fun close()

Releases every mapping, after stopping any background build.

Link copied to clipboard

Defines an index and builds it over everything already written.

Link copied to clipboard

Defines an index and builds it on the catalog's own thread, returning at once.

Link copied to clipboard
fun dropIndex(handle: IndexHandle)

Removes an index and deletes its posting files.

Link copied to clipboard
fun index(id: Int): IndexHandle?

The index with this id, or null.

Link copied to clipboard

The defined indexes, ascending by id.

Link copied to clipboard
open override fun observerFailed(cause: Throwable)
Link copied to clipboard
fun read(store: DocumentStore, handle: IndexHandle, snapshot: Snapshot): IndexReader

Opens a reader over handle at snapshot, pinning every sidecar it may consult.

Link copied to clipboard
fun readColumn(store: DocumentStore, handle: IndexHandle, snapshot: Snapshot): ColumnReader

Opens a reader over the shredded column handle at snapshot, pinning every sidecar it needs.

Link copied to clipboard

Discards every sidecar and rebuilds them from the segments, keeping the index definitions.

Link copied to clipboard
open override fun retain(liveSegments: Set<Long>)
Link copied to clipboard

Cancels every background build and waits for the one in flight to reach a segment boundary.

Link copied to clipboard
open override fun toString(): String