Summary
Vespa’s storage plane is one binary, storaged, playing two roles decided by config: distributor or storage (service layer) node. The persistence module owns only the contract: storage::spi::PersistenceProvider, the seam between the storage layer and whatever actually stores documents. The storageserver module owns only the process glue that turns StorServerConfig into a DistributorProcess or a ServiceLayerProcess; the real storage logic (bucket databases, the visiting runtime) lives in the adjacent storage framework module, and concrete backends implement the SPI elsewhere — proton’s PersistenceEngine extends storage::spi::AbstractPersistenceProvider and storage::spi::BucketExecutor. vdslib is the shared vocabulary module: Java-side Distribution, ClusterState, NodeState, and VisitorStatistics, plus small C++ containers like vdslib::Parameters. Visitors are the bulk-read plane: dynamically loaded plugins that stream a bucket’s serialized documents through plugin logic. This note is the node-local counterpart to messagebus-bucket-distribution-and-ideal-state (“Zettel - MessageBus Routing, BucketId Prefix Splitting, and Ideal State Convergence”), which covers how requests find buckets and how replicas converge; here we cover what runs inside the node that receives them.
Layer map (storage vs proton/searchcore boundary)
- persistence/: interface only. README: “the persistence SPI and a dummy implementation” (dummyimpl plus a conformancetest). The SPI covers bucket-scoped writes (putAsync, updateAsync, removeAsync, removeIfFoundAsync), reads (get, createIterator/iterate/destroyIterator), bucket admin (listBuckets, getBucketInfo, getModifiedBuckets, split, join), and cluster-state push (setClusterState). Write timestamps are generated by the distributor, so ordering lives above the SPI.
- storageserver/:
StorageApp::createProcessreadsStorServerConfig: ifisDistributorit builds aDistributorProcess(holding aDistributorNodeplusVisitorDispatcherConfig); otherwise a service layer process (DummyServiceLayerProcessfor provider types STORAGE/DUMMY).ServiceLayerProcessholds theServiceLayerNode, aVisitorFactory::Mapof external visitors, andsetupProvider()/getProvider()returning thespi::PersistenceProvider. - searchcore (proton): implements the SPI per node. Boundary: proton owns the per-node searchable structures (indexes, attributes, document store); the storage layer above owns placement, redundancy, and cluster-state reactions. Neither crosses: storage code never touches proton internals; proton never does placement. Nodes of both kinds form content-nodes.
- vdslib/: Java
Distributionsubscribes toStorDistributionConfig/DistributionConfigand exposesgetRootGroup()/getRedundancy();state/models cluster and node state;VisitorStatisticscounts buckets, documents, and bytes visited vs returned. It is the vocabulary that config-admin-delivered distribution configs and the C++ storage side agree on.
Visitor framework
storage::Visitor self-describes as “a piece of code existing in a shared library linked in, that iterates serialized documents from the persistence layer”. The base class drives per-bucket SPI iteration (BucketIterationState), feeds HitCounter::updateVisitorStatistics(vdslib::VisitorStatistics), and returns data via VisitorMessageHandler. VisitorThread runs each visitor single-threaded and keeps a LibMap of loaded visitor libraries; sibling files provide count, dump, recovery, and reindexing visitors. streamingvisitors contributes streaming::SearchVisitor : public storage::Visitor: it builds a streaming query, matches via VSM, ranks via RankManager/RankProcessor, produces docsums, and returns a documentapi::QueryResultMessage. Per SearchVisitorProtocol.html, the client sends createVisitor with VisitorLibrary: "SearchVisitor" (the default is a dump visitor), and “the distributors in VDS do not have dispatch capability” — the QRS client merges partial results. Search, recovery, reindexing, count, and dump all ride the same SPI iteration contract.
Open questions
- What does
VisitorDispatcherConfigactually make the distributor do — split bucket sets across nodes for one visit? - Who pushes document selection down into SPI
Selection: service layer chains or the visitor itself? - Are vdslib’s Java
SearchResult/DocumentSummarylegacy, or still on the visitor reply path? - How do SPI remove entries interact with visits that include removes (tombstone visibility)?
- Does proton’s
BucketExecutorregistration alter visit/merge concurrency guarantees?