Repository navigation
atelet: clear node-local caches after a node reboot #3376
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -254,9 +254,30 @@ func main() { | |
| imageCredsKeychain = kc | ||
| } | ||
|
|
||
| // Validated before the caches are cleared, so a directory the flags | ||
| // reject is never emptied. | ||
| if err := validateImageCacheGCFlags(); err != nil { | ||
| serverboot.Fatal(ctx, "Invalid image cache GC flags", err) | ||
| } | ||
| if err := validateSandboxAssetCacheFlags(); err != nil { | ||
| serverboot.Fatal(ctx, "Invalid sandbox asset cache flags", err) | ||
| } | ||
| if err := validateSnapshotCacheFlags(); err != nil { | ||
| serverboot.Fatal(ctx, "Invalid snapshot cache flags", err) | ||
| } | ||
| bootID, err := bootid.Read(bootid.Path) | ||
| if err != nil { | ||
| serverboot.Fatal(ctx, "Failed to read the node's boot ID", err) | ||
| } | ||
| cleared, err := clearCachesAfterReboot(bootID, ateletpath.CacheBootIDFile, nodepath.ActorsDir, nodepath.WorkersDir(), | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤖 should-fix 🟡 – This runs before
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. The three cache flag validations now run before the clear, so a rejected directory is never emptied. The sandbox asset dir's hard-link check still runs inside |
||
| *imageCacheDir, *sandboxAssetCacheDir, *snapshotCacheDir) | ||
| if err != nil { | ||
| serverboot.Fatal(ctx, "Failed to clear the caches after a reboot", err) | ||
| } | ||
| if cleared { | ||
| slog.InfoContext(ctx, "Cleared the node-local caches after a node reboot") | ||
| } | ||
|
|
||
| imageCache, err := imagecache.New(*imageCacheDir, | ||
| imagecache.WithKeychain(imageCredsKeychain), | ||
| imagecache.WithLocalhostRegistryReplacement(*localhostRegistryReplacement), | ||
|
|
@@ -271,9 +292,6 @@ func main() { | |
| go newImageCacheGC(imageCache, *imageCacheDir).Run(ctx) | ||
| } | ||
|
|
||
| if err := validateSandboxAssetCacheFlags(); err != nil { | ||
| serverboot.Fatal(ctx, "Invalid sandbox asset cache flags", err) | ||
| } | ||
| sandboxAssetCache, err := openSandboxAssetCache(ctx, *sandboxAssetCacheDir, *sandboxAssetCacheMinAge, *sandboxAssetFetchTimeout, nodepath.ActorsDir, nodepath.WorkersDir()) | ||
| if err != nil { | ||
| serverboot.Fatal(ctx, "Failed to open sandbox asset cache", err) | ||
|
|
@@ -286,9 +304,6 @@ func main() { | |
| if err := filecachegc.Start(ctx, ateattr.FileCacheSandboxAsset, sandboxAssetCache, sandboxAssetCacheGCFlags, fileCacheInstruments); err != nil { | ||
| serverboot.Fatal(ctx, "Failed to start the sandbox asset cache GC", err) | ||
| } | ||
| if err := validateSnapshotCacheFlags(); err != nil { | ||
| serverboot.Fatal(ctx, "Invalid snapshot cache flags", err) | ||
| } | ||
| snapshotCache, err := openSnapshotCache(ctx, *snapshotCacheDir, *snapshotCacheMinAge, *snapshotCacheFetchTimeout) | ||
| if err != nil { | ||
| serverboot.Fatal(ctx, "Failed to open snapshot cache", err) | ||
|
|
@@ -385,10 +400,6 @@ func main() { | |
| defer ateapiConn.Close() | ||
| workerService := ateapi.NewWorkerServiceClient(ateapiConn) | ||
|
|
||
| bootID, err := bootid.Read(bootid.Path) | ||
| if err != nil { | ||
| serverboot.Fatal(ctx, "Failed to read the node's boot ID", err) | ||
| } | ||
| // The snapshot tracker's workers get a context of their own, cancelled only | ||
| // once the gRPC server has drained, so background snapshot work keeps | ||
| // running while in-flight RPCs finish. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,7 @@ cannot cross mounts. | |
| | `workers/<pod uid>/default-runtime/<hash>/` | The worker's default runtimes, one directory per default its file advertises, by bundle hash, one per sandbox class, linked in from the cache by the prewarmer. These links are what keeps a default runtime cached while no actor runs it; the syncer removes a directory when the file stops naming its runtime, and they all go with the worker's directory when the pod does. | | ||
| | `sandbox-asset-scratch/` | Short-lived destinations for fetches that have no actor and no worker directory to link into: the assets the worker sandbox syncer stages when checking a version (and probes `runsc` for CPU features). Wiped at startup. | | ||
| | `sandbox-runtime-index.json` | The runtime index serialized: every runtime a worker on this node has advertised, by hash, with the asset record the hash was computed from. | | ||
| | `cache-boot-id` | The node's boot ID when `atelet` last started. A different ID at startup means the node rebooted, so `atelet` empties the cache first. | | ||
|
|
||
| A cache entry is keyed by the asset's sha256, so the same binaries under | ||
| another name or from another bucket are one entry. The store never | ||
|
|
@@ -129,6 +130,9 @@ which directories exist. Nothing counts references and nothing prunes. | |
| restart the directories of pods still on the node keep protecting their | ||
| defaults with no wait on the syncer, and the startup sweep removes the | ||
| directories of pods that are gone. | ||
| - Cache writes aren't synced, so after a node reboot atelet empties the cache | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤖 nit 🟢 – The "What is on disk" table above lists every file under
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a |
||
| and removes every worker's `default-runtime/` directory at startup. The | ||
| syncer and the prewarmer then rebuild them as on a new node. | ||
|
|
||
| The old default's directory is removed as soon as the file names the new | ||
| default: by then the worker has registered the new default (and unless an | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤖 question 🟢 – A missing marker only records the boot, so the first start of this build on a node torn by an earlier reboot keeps serving the torn entries until the node reboots again. The description says deploying this onto the node main had left torn recovered it. Was that node rebooted between? If staying torn until the next reboot is the intended trade-off, it is worth saying so in the description, since operators hitting #3351 today will upgrade onto exactly such nodes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's the intended trade-off. A missing marker only records the boot, because actors may be running on the caches during the upgrade. So a node that is already broken stays broken until it reboots again. The recovery claim in the description was wrong, and I removed it. The description now says such a node needs one reboot or a replacement after upgrading.