Skip to content

Commit f053f8b

Browse files
committed
Some more updates
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
1 parent 094ef25 commit f053f8b

1 file changed

Lines changed: 62 additions & 16 deletions

File tree

  • proposals/0003-incremental-snapshots

proposals/0003-incremental-snapshots/README.md

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@ example a WebAssembly module, and then runs the code of the customer.
2929

3030
Caching a snapshot for the top N customers would remove that work from most
3131
requests. 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

3635
With incremental snapshots, a customer snapshot holds only the pages that the
3736
state 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

8085
The snapshots make a tree. A sandbox can restore any snapshot and then make
8186
more 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.
98101
4. 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.
123127
Every layer except the one with the restore page tables must give at least one
124128
live 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

128141
This 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
132145
version 1 images, and makes their single blob one layer. Thus old snapshots
133146
still 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

Comments
 (0)