@@ -29,9 +29,8 @@ example a WebAssembly module, and then runs the code of the customer.
2929
3030Caching a snapshot for the top N customers would remove that work from most
3131requests. The memory cost makes this impractical today, because every snapshot
32- stores a full copy of the sandbox memory. N cached customers means N copies of
33- the WebAssembly runtime, while the customer state is a small fraction of each
34- one.
32+ stores a full copy of the sandbox memory, despite the specific customer state
33+ is a small fraction of each one.
3534
3635With incremental snapshots, a customer snapshot holds only the pages that the
3736state of the customer changed. All N snapshots share the blob of the base
@@ -70,18 +69,22 @@ not hold page 2. Both snapshots share blob 0. Only page 2 is a copy.
7069* A ` SnapshotLayer ` has one blob and the ranges in that blob that the layer
7170 gives. Each layer has its own ranges. Layers in different snapshots can share
7271 a blob.
73- * A ` SnapshotBlob ` is immutable storage. It holds guest data and then page
74- tables. Either part can be absent. All snapshots that use this data share the
75- blob.
76-
77- The live ranges of the layers do not overlap. Thus at most one layer holds each
78- address, and a lookup does a binary search.
72+ * A ` SnapshotBlob ` is one immutable, contiguous block of host memory. It has two
73+ parts, guest data and page tables. All snapshots that use this data share the
74+ blob. A blob has no data
75+ when every mapped page is already in a live range, for example when the host
76+ calls ` map_region ` or ` map_file_cow ` and then takes a snapshot. Every layer
77+ contains page tables, but only the last layer's page tables are needed for a
78+ restore.
79+
80+ The data ranges of the blobs do not overlap, thus at most one layer holds each
81+ address. The layers are sorted by the start address of their data range. A
82+ lookup can then binary search them, and a new blob can take the first gap that
83+ is large enough.
7984
8085The snapshots make a tree. A sandbox can restore any snapshot and then make
8186more snapshots from it. Snapshots with the same parent share the blobs of that
82- parent. No snapshot points to its parent, because each one has the full list of
83- its layers. Thus you can delete one snapshot, and the others keep the blobs
84- that they use.
87+ parent.
8588
8689### Taking a Snapshot of a Sandbox
8790
@@ -97,8 +100,9 @@ that they use.
97100 the page tables are built again. A shared page keeps its address.
981014 . Make the list of layers. The new blob is one layer, and it gives the page
99102 tables for a restore. This snapshot keeps each parent layer that still gives
100- at least one page, without the pages that moved into the new blob. It drops
101- a parent layer that gives no page.
103+ at least one page. Such a layer keeps its blob, and its live ranges lose the
104+ pages that moved into the new blob. This snapshot drops a parent layer that
105+ gives no page.
102106
103107### Restoring a Sandbox to a Snapshot
104108
@@ -123,6 +127,15 @@ fixed cap on its total mappings, and taking a snapshot above the cap fails.
123127Every layer except the one with the restore page tables must give at least one
124128live range, so the cap bounds the layer count too.
125129
130+ The page tables in the layers before the last waste space, in memory and on
131+ disk, unless the snapshot that created them is also kept.
132+
133+ ### API
134+
135+ No public API changes, except ` PtRootFinder ` . It received the flat snapshot
136+ buffer, which no longer exists, so it now receives a reader that takes a guest
137+ physical address.
138+
126139### Snapshots on Disk
127140
128141This builds on the OCI image format that snapshots already use. The image has
@@ -132,11 +145,44 @@ emits the version 2 config and memory media types. The loader still accepts
132145version 1 images, and makes their single blob one layer. Thus old snapshots
133146still load.
134147
148+ Each snapshot is one manifest that lists every blob it needs by digest. Two
149+ snapshots that share a blob name the same digest. The layout stores that file
150+ once.
151+
152+ Snapshot A and snapshot B saved to one directory:
153+
154+ ```
155+ index.json tag a -> manifest A, tag b -> manifest B
156+ blobs/sha256/<mA> manifest A: config <cA>, layers <b0>
157+ blobs/sha256/<mB> manifest B: config <cB>, layers <b0> <b1>
158+ blobs/sha256/<cA> config A: layer 0 live p0 p1 p2 p3
159+ blobs/sha256/<cB> config B: layer 0 live p0 p1 p3, layer 1 live p2'
160+ blobs/sha256/<b0> blob 0
161+ blobs/sha256/<b1> blob 1
162+ ```
163+
164+ Deleting tag a leaves blob 0 in place, because manifest B still names it.
165+
135166#### Limitations
136167
137- * Snapshots on disk only share blobs if they are saved in the same directory.
138- That directory is the ` path ` of ` Snapshot::save ` , or the target of
168+ * Blobs are shared inside one OCI layout, because that layout is the blob
169+ store. Saving the same snapshots to a second layout writes a second copy of
170+ each blob. The layout is the ` path ` of ` Snapshot::save ` , or the target of
139171 ` oras cp --to-oci-layout <ref> <dir>:<tag> ` .
140172
173+ ## Alternatives considered
174+
175+ * Separate page tables from memory blobs. This would make it so a snapshot
176+ only carries the 1 page table it needs, rather than including page tables
177+ for all layers. A future plan is for the guest to read the page tables from
178+ the mapped blob, instead of from the copy that a restore puts in scratch. A
179+ separate blob would then need its own mapping, which costs one more mapping
180+ per snapshot and two hypercalls per restore. That argument is weak today,
181+ because nothing maps the page tables yet.
141182
183+ ## Future work
142184
185+ An API to compact a snapshot would rebuild it as a single layer. The new layer
186+ holds only the live pages, so the snapshot drops its dead pages and its extra
187+ page tables. It also needs one mapping instead of many, which gives a long
188+ chain room under the cap. This is out of scope for this HIP.
0 commit comments