Paying for Retrieval Across Storage and Shards

Count remote bytes and dependent reads, prove a shard merge, and revise a search architecture when growth, cold caches and failures change its budget.

A query node disappears. Its replacement can reach the durable database, but its cache is empty. Meanwhile, the service has grown from thousands of documents to millions. Does adding more query nodes restore the latency target, or do those nodes compete for the same remote bytes?

We can answer part of that question on paper. Count dependencies, requests and bytes separately. Then check what a complete result requires when those bytes come from several shards. A small calculated latency is useful evidence about a plan’s assumptions; it is insufficient evidence about end-to-end p99.

These costs cross both paths on the primer’s search-system map. Review its execution and resources vocabulary when distinguishing logical work, storage traffic and request dependencies.

This capstone continues the durable-write and snapshot chapter. Your exit artifact is a resource table, a failure contract and an architecture revision you can defend. Reading time covers the prose; allow extra time for the exercises and your own trace.

Evidence boundary: All workloads, capacities, request plans, shard distances and recovery choices below are synthetic or stipulated. Public mechanisms are attributed to passages inspected on October 1, 2026. No engine benchmark, provider-price comparison, fault injection or learner assessment ran. The rolling documentation is not a pinned implementation of turbopuffer’s in-progress v3 migration.

Grow the workload without changing the answer

Our engineering-document service keeps stable doc IDs, versions, tenant membership and live/deleted state. It now stores a title vector and a body vector, each 768-dimensional float32: 768 × 4B = 3072B = 3KiB. The non-vector payload is 12KiB. Storing it once gives a limited stable-layout total of 12 + 2×3 = 18KiB/document.

The following table changes the scale explicitly. Rates are constant for the calculations, not measured service capacity.

workloaddocumentsqueries_per_supdates_per_s
initial500001020
grown5000000010002000

The new requirement is a 150ms end-to-end p99 target, including client/service overhead. In a measured latency distribution, p99 is the value at or below which 99% of requests complete. A mean-model sum does not establish that percentile. We also stipulate that 90% of queries touch 1% of tenants and that nodes can be replaced cold. Tenant skew motivates cache-local routing; it supplies no byte cache-hit percentage by itself.

A dense query still selects exactly one field, body or title, with one vector per document in that field. Rank distinct documents by squared Euclidean distance ascending, then doc ID ascending. The tenant comes from trusted identity context. Live state and query predicates are evaluated at the selected snapshot; a trusted current-policy authorization decision precedes content disclosure or external reranking. These requirements survive growth. Faster I/O cannot fix a wrong version, an ineligible hit or an unauthorized disclosure.

Predict the remote critical path

A request asks remote storage for data. A round groups requests that can start together because their addresses are already known. If the answer to request R1 tells us which address to fetch in R2, R2 is dependent: it cannot start with R1. The critical path is the longest dependency chain that must finish before the answer is ready.

This distinction appears in turbopuffer’s architecture account. It describes fetching centroid information, then fetching selected clusters in parallel. Its fuller cold path also includes storage metadata, filtering indexes and recent unindexed data. The planner trades extra dependent roundtrips against fetching more data in an existing round. The document’s example timings are vendor observations, not constants for our service or for every object-storage class.

Consider two smaller synthetic plans. Plan A discovers one address at a time and transfers only what it needs. Plan B already knows three ranges from compatible local routing metadata and eagerly fetches them together, including unused data. This assumption is what removes the dependencies; sending requests concurrently cannot remove a dependency on an unknown address.

planrequestsroundstotal_mib
A331
B314
The same request count, different dependencies

Plan A: three dependent rounds

  1. R1: routing, 128KiB

    Read routing metadata to discover the next query range.

  2. R2: candidates, 768KiB

    Read the selected query range, score candidates and identify payload addresses.

  3. R3: projection, 128KiB

    Fetch the needed result fields. Total transfer is 1MiB.

Plan B: one eager round

  1. Known addresses before I/O

    Compatible local metadata supplies three ranges, including speculative payload data.

  2. R1, R2 and R3 run in parallel

    One remote round transfers 4MiB in aggregate; parallel requests share bandwidth.

  3. Score and select projection locally

    Use the returned data; discard the unnecessary material.

Synthetic plans. Arrows in A mean the next address depends on the previous response. B's three addresses are known before its round starts. Query work and payload projection remain different work.

In words, A waits for routing, then candidates, then projection; its request sizes sum to 1MiB. B fetches three known ranges at once and selects locally from 4MiB. Both issue three requests. B reduces dependency latency while spending more bandwidth. Neither plan includes freshness/authorization work unless those inputs are already available under the stated contract.

Stipulate 20ms of latency per round, perfect request parallelism within a round, and one effective aggregate transfer rate shared by its requests. Latency and transfer do not overlap in this toy. Thus:

modeled I/O milliseconds = rounds × 20 + total MiB / aggregate MiB/s × 1000

Giving each parallel request the full aggregate rate would count capacity three times. Real concurrency, latency/transfer overlap, retries and queueing need measurements rather than this formula.

Guided change: lose bandwidth or lose the routing cache

Predict which plan wins at 100MiB/s. Then reduce the aggregate rate to 10MiB/s. Finally restore 100MiB/s but make B’s routing metadata cold: one additional dependent metadata round is required. For this sensitivity case only, its byte size is stipulated negligible, so B keeps 4MiB. Count its two rounds before checking the answer.

caserate_mib_sround_msA_roundsB_roundsA_mibB_mib
fast100203114
slow10203114
cold-routing100203214
Check the reversal and the missing dependency
caseA_msB_mspreferred
fast7060B
slow160420A
cold-routing7080A

At 100MiB/s, A takes 60+10=70ms; B takes 20+40=60ms. At 10MiB/s, A takes 60+100=160ms; B takes 20+400=420ms. Saving two rounds no longer pays for 3MiB of extra transfer. With cold routing, B takes 40+40=80ms, so A wins again.

With the original round counts, the plans tie at 75MiB/s: B saves 40ms and transfers 3MiB more. A pass includes all three totals, three requests in each original plan, shared transfer capacity, and the address-dependency assumption. None of these numbers proves p99 or a monetary saving.

Turn document changes into a resource budget

The SlateDB overview offers an accessible object-storage LSM example. A log-structured merge tree, or LSM, buffers key/value changes, persists sorted tables and merges tables through compaction. Its write-ahead log supports crash recovery; its manifest identifies published files and recovery state. This is SlateDB’s documented design, not proof that turbopuffer uses SlateDB.

Those mechanisms explain why raw payload bytes are only one layer. Updating a 3KiB vector may serialize keys/versions, append a log record, change ANN membership, update retained cluster-local summaries, flush tables and later rewrite data during compaction. Stable doc-ID text/tenant postings can remain unchanged when their logical values do. A real tenant or text change still changes those indexes. The identity chapter’s reference trace supplies that distinction.

Keep the layers separate: logical changes; KV-serialized material; WAL/flush/compaction bytes; remote requests/bytes; CPU; and end-to-end latency. The following saved inputs count uncompressed vector/payload material only. Vector-only updates are logical patches; their 3KiB excludes version/key metadata and any required reconstruction of the complete document state.

parametervalueunit
vectors_per_doc2vectors
vector_kib3KiB/vector
payload_kib12KiB/document
full_update_fraction0.25fraction of updates
index_pause_s120seconds
index_service_per_s2500updates/s
document_shards4shards
candidates_per_shard1024selected-field vectors
global_k10documents
byte_cache_hit0.95fraction of requested bytes
remote_read_budget_mib_s512MiB/s
cold_transfer_mib_s100aggregate MiB/s
remote_round_ms20milliseconds
cold_rounds3rounds
collapsed_rounds1round
end_to_end_target_ms150milliseconds at p99

Use binary units: 1KiB is 1024B; 1MiB is 1024KiB; 1GiB is 1024MiB. Before adding indexes, replicas, compression or metadata, 50,000 documents occupy about 0.8583GiB and 50 million about 858.3069GiB. These are model quantities, not a cache-sizing recommendation.

If every grown-workload update replaces all 18KiB, raw ingest is 2000 × 18 / 1024 = 35.15625MiB/s. Our mixed workload instead uses 25% full replacements and 75% single-vector patches. Its mean is 0.25×18 + 0.75×3 = 6.75KiB/update, or 13.18359375MiB/s. Raw bytes have changed even though the update count has not.

During a 120s indexer pause, 2000 × 120 = 240,000 updates accumulate. A restored indexer serving 2,500 updates/s must also handle the incoming 2,000, leaving 500/s for catch-up. Drain time is 240,000 / 500 = 480s. That capacity assumes the same update-cost mix and constant rates. If service equals arrival rate, the backlog never drains; if it is lower, backlog grows.

The backlog contains 240,000 × 6.75 × 1024 = 1,658,880,000B of stipulated raw material. That is an aggregate across this service, not a per-query tail or a per-namespace backlog. The bytes alone do not determine the recent-write scan cost for a tenant. Index publication, shard/tenant placement and query mode determine what each query must reconcile.

Charge candidate work and projection separately

Partition the corpus into four document shards: disjoint sets with one owner for each document and both of its vector fields. Stipulate that each query reads 1,024 selected-field candidates per shard, at 3KiB/vector. Candidate work totals 4 × 1024 × 3KiB = 12MiB. After global selection, fetch ten 12KiB payloads: 120KiB, or 0.1171875MiB. Together they request 12.1171875MiB/query, before routing, filtering, tail and index metadata.

Candidate bytes buy ranking work; projection bytes buy requested output fields. Sending candidates from every shard does not require projecting every local candidate. Global selection can avoid unnecessary payload fetches, at the possible cost of a dependent projection round. The fixed 1,024 candidate budget describes work, not a proof of exact-neighbor coverage.

At 1,000 queries/s with a stipulated byte-weighted cache hit rate of 95%, expected remote reads are 1000 × 12.1171875 × 0.05 = 605.859375MiB/s. That already exceeds the 512MiB/s aggregate read budget. Writes, compaction and cache bandwidth have additional budgets. Parallel shard reads can finish sooner for one request while saturating a shared resource across many requests.

A byte hit rate measures avoided remote bytes divided by requested bytes. A count of queries, objects or requests that hit cache is a different statistic. Nor does “90% of queries touch 1% of tenants” imply 95% byte hits: those tenants might have large working sets, cold payloads, frequent changes or poor admission/eviction behavior. Measure hit/miss bytes by data type and tenant distribution.

quantityvalueunit
initial_payload0.858306884765625GiB
grown_payload858.306884765625GiB
all_full_raw_ingest35.15625MiB/s
mixed_mean_update6.75KiB/update
mixed_raw_ingest13.18359375MiB/s
pending_updates240000updates
pending_raw_material1658880000B
net_catch_up500updates/s
drain_time480seconds
candidate_reads12MiB/query
global_projection0.1171875MiB/query
total_query_reads12.1171875MiB/query
expected_remote_reads605.859375MiB/s
cold_transfer121.171875ms
cold_three_rounds181.171875ms
cold_one_round141.171875ms

This resource table exposes two different problems. A cold query transfers 12.1171875 / 100 × 1000 = 121.171875ms worth of data. With three 20ms rounds, its modeled I/O alone is 181.171875ms, above the 150ms target. Hypothetically collapsing that to one round while holding bytes fixed gives 141.171875ms, before CPU, authorization, client overhead and queueing.

The smaller sum does not establish feasibility. Holding bytes fixed while removing dependencies is a sensitivity calculation; it does not construct an achievable plan. Projection ordinarily waits for winners. Eager fetching can remove that wait while increasing bytes, as Plan B showed. Measured cold/warm latency distributions, concurrent throughput and failure/recovery behavior must establish whether a concrete plan meets end-to-end p99.

Prove the shard merge before trusting fanout

Why can each shard return only its local document top-k? Assume every required shard responds, each evaluates the same snapshot and eligibility predicate, documents have one owner, and every shard uses the same exact comparable total order.

Take a document omitted from its shard’s exact top-k. At least k distinct eligible documents in that same shard precede it in the total order. They also precede it globally. The omitted document cannot belong to the global top-k. Therefore, union the local document top-k lists, sort by the same order and take k. Tie handling matters: “precede” means (distance, doc_id), not distance alone.

Use this tiny, separate synthetic snapshot S=42. Every row is live, belongs to trusted tenant A and supplies the selected body field’s saved squared distance. The toy asks for k=2; the earlier resource worksheet asks for k=10.

sharddoc_idtenantdistance
s01A0.20
s05A0.30
s09A0.90
s12A0.10
s16A0.35
s110A0.80
s23A0.20
s27A0.40
s211A0.85
s34A0.25
s38A0.50
s312A0.95

The local top-2 lists are s0 [1,5], s1 [2,6], s2 [3,7], s3 [4,8]. Their sorted union is [2,1,3,4,5,6,7,8]; the global top-2 is [2,1]. Doc1 wins the boundary tie with doc3. The full-table exact oracle gives the same answer. Taking the best result from just one shard has no such proof.

Guided merge failure: a shard disappears

Predict the observed result with s1 missing. Can the coordinator call it complete because it still has two IDs? Also ask for k=20 with every shard present, then query tenant C. Use the full saved table, rather than assuming a result count proves completeness.

casektenantmissing_shard
complete2A-
lost-s12As1
fewer-than-k20A-
empty2C-
Check complete, unavailable and smaller populations
casestatusresponse_idsobserved_ids
completecomplete[2,1][2,1]
lost-s1unavailable[][1,3]
fewer-than-kcomplete[2,1,3,4,5,6,7,8,10,11,9,12][2,1,3,4,5,6,7,8,10,11,9,12]
emptycomplete[][]

The surviving shards produce [1,3], missing doc2, so sufficient count does not imply completeness. Our stipulated response contract returns unavailable with no ordinary result payload. The empty response IDs here are an error outcome, not a claim that tenant A has no eligible documents. A product may separately permit explicitly labeled incomplete results, but must communicate the missing-shard limitation rather than return them as complete success.

With all shards present, k=20 returns all twelve eligible documents. Tenant C has none and legitimately receives a complete empty result. A pass gives the IDs, tie order, required shard set and response status, and distinguishes unavailable from an empty corpus.

The proof has a narrow boundary. Approximate shard searches can omit better documents; exact global merging cannot repair those omissions. Different snapshots can merge different logical database states. If documents overlap across shards, merge needs deduplication and agreement on each document’s version and score. Overlap alone does not defeat local exact distinct-document top-k sufficiency when that agreement and one total order hold.

Taking vector slots is a different contract: vector top-2 [d1,d1] need not contain document top-2 [d1,d2]. Compound or late-interaction scoring needs a defined document score and aggregation before applying the document proof. Our single-field, one-owner contract keeps both ownership reconciliation and multi-vector aggregation out of this exercise.

Lexical scoring has another boundary. BM25 uses corpus statistics such as document frequency and length normalization. If shards compute different shard-local statistics, their raw scores need not represent one global ranking function. Define shared statistics or a different explicit ranking contract before applying a comparable-order merge argument. Parallelism does not make unlike scores comparable.

Define what survives node loss

turbopuffer’s architecture describes NVMe/memory caches and routing toward a node that already has a namespace cached; any query node can serve any namespace. Its Guarantees page identifies object storage as its stateful dependency and says consistency is preferred over availability when that storage is unreachable, with query configuration able to change the tradeoff. These are named product statements, not a universal failure protocol.

Our model keeps committed data and valid published state authoritative; caches are disposable copies. Losing a cache node changes temperature. Recovery loads a valid base and the committed tail needed for the selected snapshot; it does not promote whichever cached file or highest version happens to survive. S3’s documented consistency model provides strong reads after successful object writes and atomic single-key updates, while explicitly excluding atomic updates across keys. The database’s valid publication/recovery boundary is still required.

One update, one query, and the failure branch

Tenant-scoped update

  1. Validate A and doc17 v2

    Trusted identity authorizes the body-vector patch; retain the title vector and stable payload.

  2. Commit at position 41

    Persist the change under the stipulated ordered-write authority before acknowledgment.

  3. Maintain derived state

    The owner s2 applies version/index changes asynchronously; pure movement leaves unchanged doc-ID postings stable.

Filtered body query

  1. Pin valid metadata: I=40, S=42

    Every shard selects the same boundary and reconciles committed tail changes through 42.

  2. Query candidates, then merge

    Apply tenant/live/tag predicates; rank distinct latest documents using one comparable order.

  3. Project, authorize, disclose

    Fetch global winners' snapshot versions; use current trusted policy before content leaves the service.

Failure branch

  1. Cache node lost

    Load from valid durable authority; account for cold metadata, candidates and projection.

  2. Required shard missing

    Return unavailable rather than claiming a complete top-k from surviving shards.

  3. Required object authority unreachable

    Fail the fresh/complete read; extra stateless nodes cannot recreate the unavailable dependency.

Stipulated capstone flow, not a vendor protocol. Metadata chooses valid read state; candidate scoring chooses IDs; projection retrieves selected content. Failure to establish required authority or shard coverage stops complete success.

The trace’s equivalent in prose is precise. A’s body-vector patch commits for doc17, owned by s2, with both named fields remaining under that owner. It advances the document version while leaving logical text/title/tenant values unchanged. The authority must reconstruct its complete state from the patch; the raw-byte worksheet omits that overhead. At I=40/S=42, the query includes the committed position 41 through the tail until a compatible base incorporates it. Reconciliation suppresses the old vector before ranking. Tenant A, live state and the requested tag predicate determine snapshot eligibility across all required shards. Global winner IDs drive projection from those selected versions, followed by the current-policy disclosure check.

Separate recovery cases from that successful trace:

FailureRequired action in this modelWhat the resource model does not establish
Disposable cache node disappearsRoute to a replacement, load valid read state and include the required tail.Cache warm-up time, load spikes or p99 after replacement.
Indexer pauses but committed storage is reachablePreserve committed authority; search required recent state or fail if the freshness contract cannot be met.That tail search stays within the latency target.
One required document shard is unavailableReturn explicit unavailable; restore that shard’s valid state before complete success.A complete answer from a sufficient number of surviving hits.
Required object-store state is unreachableFail the fresh/complete request under this contract.Availability restored merely by adding query nodes.

The Query API’s mode-specific contract says strong queries search all unindexed writes; eventual queries search at most 128MiB and can be stale. Its multi-queries share one snapshot. The Guarantees page explicitly says strong sharded queries use one snapshot across shards while eventual queries may reflect different times per shard. Our exact merge exercise assumes the former kind of shared boundary; choosing stale or mixed-time reads changes the answer contract. Neither contract settles ANN fidelity or disclosure permission.

Independent changed-input capstone

Keep 50 million documents, 1,000 queries/s, 2,000 updates/s, the four-shard work model and 120s pause. Now change byte hits to 90%, full replacements to 50% of updates, and restored indexing service to 2,200 updates/s. One required shard is unavailable during a cold replacement. The 150ms p99 target remains.

parametervalueunit
byte_cache_hit0.90fraction of requested bytes
full_update_fraction0.50fraction of updates
index_service_per_s2200updates/s
missing_shards1required shard

Before checking, recalculate the complete four-shard workload after restoration, rather than claiming a capacity win from dropping the failed shard. Write its remote reads, mean update bytes, raw ingest, pending raw bytes, net catch-up and drain time. State the response while the shard is missing and the authority used for recovery. If indexing service falls to 2,000 or 1,900 updates/s, say what happens to the backlog.

Then trace one tenant-scoped body-vector update and one filtered body query through durable storage, base+tail reconciliation, index maintenance, shards, global selection, projection and disclosure. Choose an architecture revision, reject one plausible alternative, and name a measurement that would change your decision. A list of product names or the assertion “cache more” does not satisfy this artifact.

Check the changed budget and a defensible revision
quantityvalueunit
mean_update10.5KiB/update
raw_ingest20.5078125MiB/s
pending_updates240000updates
pending_raw_material2580480000B
net_catch_up200updates/s
drain_time1200seconds
total_query_reads12.1171875MiB/query
expected_remote_reads1211.71875MiB/s

The mean update is 0.5×18 + 0.5×3 = 10.5KiB. At 2,000/s, that is 20.5078125MiB/s. The 240,000-update backlog now contains 2,580,480,000 raw bytes. Net service is 2200−2000=200/s, so drain takes 1,200s. At service 2,000/s there is no drain; at 1,900/s backlog grows by 100 updates/s. A changed cost mix can invalidate the stipulated service rate itself; the calculation assumes it has already been established for this mix.

Query work remains 12.1171875MiB when all four shards return. Ten percent remote misses require 1211.71875MiB/s, over twice the 512MiB/s read budget, before omitted work. A missing shard returns unavailable, even if surviving shards supply k hits. Restore its state from valid published state and committed authority, reconcile the required snapshot, then resume complete responses. Losing a cache alone calls for warming/reloading; losing object-store reachability blocks that authority path.

One defensible revision keeps stable document ownership, durable authority and replaceable caches, with routing toward warm nodes and projection after global selection. It then treats bandwidth and cold latency as unmet constraints requiring changes and measurements. For the original four-shard work, even 95% byte hits exceed the budget. A measured 97% would reduce this term to 363.515625MiB/s, leaving some read headroom; skew alone cannot justify that assumption. Alternatively, add remote-read capacity or reduce candidate/fanout bytes through a plan whose fidelity and fallback have been checked. Skipping required shards or blindly truncating candidates is not a valid complete-answer optimization.

Reject “add stateless nodes until p99 is fixed” as an adequate revision: it does not increase the stated shared read budget and can increase cold misses. Reject silently switching to stale reads when a prior edit/delete must be observed. If eventual behavior is chosen for a different application requirement, declare that change explicitly.

A useful falsifying measurement holds corpus, query predicates, update mix and result fidelity fixed, then records remote miss bytes/s, request dependencies, index lag/drain and end-to-end p99 with warm steady traffic and cold node replacement. If locality cannot sustain the proposed byte hit rate, choose added capacity or less justified query work; if collapsed dependencies require excessive eager payload reads, prefer a different plan. A pass includes the arithmetic, failure status/authority, complete data path, rejected alternative and measurement. These saved answers verify a paper model, not a learner’s independent competence or a deployed system.

Carry the whole decision forward

The series began with a map of search systems, connecting documents and representations to the read/write paths. Identity and layout then determined what moved and what needed a separate projection. Execution and filtering determined which candidate work was justified. Visibility determined which document versions had authority. Growth now makes the bytes, dependencies and failure contract explicit.

Your final artifact should let another engineer reproduce the resource table and follow one update and query without guessing the snapshot, shard ownership, score order or disclosure point. Include the unmet budget and the experiment that could change the architecture. That is a defensible design decision even before a production capacity claim is possible.

Copy these six fields into one decision note. Fill them progressively, attach each chapter’s output, and revise earlier fields when a requirement or assumption changes. Label worksheet calculations as synthetic; mark physical measurements you have not made as unknown.

  1. Workload and answer contract: What changed? Record the query/update mix, selected vector field and scoring order, snapshot eligibility, required freshness and current-policy disclosure separately.
  2. Identity and update path: Which stable IDs, payloads, postings and ownership references stay or move? Attach the identity chapter’s reference and byte trace.
  3. Query plan and coverage: Attach the lexical skip proof and exact eligible dense baseline. Record the chosen paths and cumulative fallback limits. Keep lexical scores and dense distances separate.
  4. Authority and recovery: Record the durable boundary, indexed boundary and selected query snapshot (D/I/S), winning versions and tombstones, the retry receipt, and publication/reclamation conditions. Attach the version trace.
  5. Resources and failure response: Separate logical, physical and remote cost layers. Attach the resource table, identify required shards, and declare unavailable/stale response semantics.
  6. Decision and falsifier: Choose a revision, reject an alternative, retain unmet constraints, and name the measurement and outcome that would reverse the choice while holding workload and result fidelity fixed.

For selected reading, use turbopuffer Architecture for cache-local routing and its cold request dependencies; Query and Guarantees for named freshness, shard-snapshot and object-store dependency scopes; SlateDB’s overview for WAL, sorted files, manifests and amplification; and S3’s consistency section for single-key atomicity and the multi-key boundary. These rolling documents resolve mechanisms and contracts; their example timings do not measure this workload.

Designing Data-Intensive Applications, second edition, by Martin Kleppmann and Chris Riccomini (March 2026), is a study bridge for partitioning and consistency. Database Internals, Alex Petrov (2019), is a bridge for storage and maintenance. Their full texts were not accessed for this series, so these are topic recommendations. The chapter’s merge proof is general reasoning under its declared assumptions. No full DiskANN-method claim or proprietary consensus/availability protocol is needed to defend it.

Search-systems Ch 6/6
  1. 1 A Map of Search Systems 10m
  2. 2 When an Index Becomes Your Data Model 11m
  3. 3 Doing Less Work, and Doing Work Faster 15m
  4. 4 Finding Neighbors Inside the Eligible Corpus 15m
  5. 5 From Durable Write to Searchable Snapshot 15m
  6. 6 Paying for Retrieval Across Storage and Shards 18m