@impz/testing 6.1.0 → 6.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +339 -162
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -18989,7 +18989,7 @@ function readBusyPorts() {
|
|
|
18989
18989
|
// packages/daemon/testing/start-test-impd.ts
|
|
18990
18990
|
import { createORPCClient as createORPCClient2 } from "@orpc/client";
|
|
18991
18991
|
import { RPCLink } from "@orpc/client/fetch";
|
|
18992
|
-
import
|
|
18992
|
+
import invariant7 from "tiny-invariant";
|
|
18993
18993
|
|
|
18994
18994
|
// packages/daemon/src/config.ts
|
|
18995
18995
|
import { join as join4 } from "path";
|
|
@@ -21955,7 +21955,7 @@ import { join as join47 } from "path";
|
|
|
21955
21955
|
// packages/daemon/package.json
|
|
21956
21956
|
var package_default = {
|
|
21957
21957
|
name: "@impz/daemon",
|
|
21958
|
-
version: "6.1.
|
|
21958
|
+
version: "6.1.1",
|
|
21959
21959
|
private: true,
|
|
21960
21960
|
description: "impd: the imp host daemon",
|
|
21961
21961
|
type: "module",
|
|
@@ -22314,6 +22314,7 @@ function toImpRecord(row) {
|
|
|
22314
22314
|
id: row.id,
|
|
22315
22315
|
name: row.name,
|
|
22316
22316
|
imageId: row.image_id,
|
|
22317
|
+
imageDigest: row.image_digest,
|
|
22317
22318
|
state: row.state,
|
|
22318
22319
|
kind: row.kind,
|
|
22319
22320
|
vcpus: row.vcpus,
|
|
@@ -22377,6 +22378,7 @@ async function writeImpRow(db, imp) {
|
|
|
22377
22378
|
id: imp.id ?? Bun.randomUUIDv7(),
|
|
22378
22379
|
name: imp.name,
|
|
22379
22380
|
image_id: imp.imageId,
|
|
22381
|
+
image_digest: imp.imageDigest,
|
|
22380
22382
|
state: "creating",
|
|
22381
22383
|
vcpus: imp.vcpus,
|
|
22382
22384
|
memory_mib: imp.memoryMib,
|
|
@@ -24963,7 +24965,8 @@ function formatCaller(caller) {
|
|
|
24963
24965
|
class RamBudgetError extends ImpdError {
|
|
24964
24966
|
name = "RamBudgetError";
|
|
24965
24967
|
protectedImps;
|
|
24966
|
-
|
|
24968
|
+
budget;
|
|
24969
|
+
constructor(message, shortfall, budget) {
|
|
24967
24970
|
super("RAM_BUDGET_EXCEEDED", {
|
|
24968
24971
|
status: 503,
|
|
24969
24972
|
message,
|
|
@@ -24977,16 +24980,27 @@ class RamBudgetError extends ImpdError {
|
|
|
24977
24980
|
}
|
|
24978
24981
|
});
|
|
24979
24982
|
this.protectedImps = shortfall.protected;
|
|
24983
|
+
this.budget = budget;
|
|
24980
24984
|
}
|
|
24981
24985
|
}
|
|
24982
|
-
|
|
24983
|
-
|
|
24986
|
+
var BUDGET_TITLES = {
|
|
24987
|
+
ram: "the whole RAM budget",
|
|
24988
|
+
ordinary: "the RAM budget outside the huge page pool"
|
|
24989
|
+
};
|
|
24990
|
+
function buildImpOverBudgetError(budget, shortfall) {
|
|
24991
|
+
return new RamBudgetError(`the imp's memory, at its max (${String(shortfall.requestedMib)} MiB), is larger than ${BUDGET_TITLES[budget]} (${String(shortfall.budgetMib)} MiB)`, { ...shortfall, protected: [] }, budget);
|
|
24984
24992
|
}
|
|
24985
24993
|
function buildRamBudgetError(shortfall) {
|
|
24986
24994
|
const used = String(shortfall.usedMib);
|
|
24987
24995
|
const budget = String(shortfall.budgetMib);
|
|
24988
24996
|
const requested = String(shortfall.requestedMib);
|
|
24989
|
-
return new RamBudgetError(`not enough RAM: ${used} of ${budget} MiB in use, ${requested} MiB requested, and no idle imp left to sleep`, shortfall);
|
|
24997
|
+
return new RamBudgetError(`not enough RAM: ${used} of ${budget} MiB in use, ${requested} MiB requested, and no idle imp left to sleep`, shortfall, "ram");
|
|
24998
|
+
}
|
|
24999
|
+
function buildOrdinaryBudgetError(shortfall) {
|
|
25000
|
+
const used = String(shortfall.usedMib);
|
|
25001
|
+
const budget = String(shortfall.budgetMib);
|
|
25002
|
+
const requested = String(shortfall.requestedMib);
|
|
25003
|
+
return new RamBudgetError(`not enough RAM outside the huge page pool: ${used} of ${budget} MiB in use, ${requested} MiB requested on 4 KiB pages, and no idle imp left to sleep`, shortfall, "ordinary");
|
|
24990
25004
|
}
|
|
24991
25005
|
function buildHugePoolError(shortfall) {
|
|
24992
25006
|
const committed = String(shortfall.committedMib);
|
|
@@ -24997,7 +25011,7 @@ function buildHugePoolError(shortfall) {
|
|
|
24997
25011
|
usedMib: shortfall.committedMib,
|
|
24998
25012
|
requestedMib: shortfall.requestedMib,
|
|
24999
25013
|
protected: []
|
|
25000
|
-
});
|
|
25014
|
+
}, "pool");
|
|
25001
25015
|
}
|
|
25002
25016
|
function buildNeededMib(usedMib, requestedMib, budgetMib) {
|
|
25003
25017
|
return Math.max(0, usedMib + requestedMib - budgetMib);
|
|
@@ -25005,6 +25019,9 @@ function buildNeededMib(usedMib, requestedMib, budgetMib) {
|
|
|
25005
25019
|
function isRamBudgetError(error) {
|
|
25006
25020
|
return isImpdError(error, "RAM_BUDGET_EXCEEDED");
|
|
25007
25021
|
}
|
|
25022
|
+
function isOrdinaryBudgetError(error) {
|
|
25023
|
+
return error instanceof RamBudgetError && error.budget === "ordinary";
|
|
25024
|
+
}
|
|
25008
25025
|
|
|
25009
25026
|
// packages/daemon/src/identity/auth/caller-view.ts
|
|
25010
25027
|
function canSeeEveryOwner(caller) {
|
|
@@ -26450,6 +26467,7 @@ var CopyImpSchema = z62.object({
|
|
|
26450
26467
|
id: z62.string(),
|
|
26451
26468
|
name: z62.string(),
|
|
26452
26469
|
imageId: z62.string(),
|
|
26470
|
+
imageDigest: z62.string(),
|
|
26453
26471
|
state: z62.string(),
|
|
26454
26472
|
vcpus: z62.int(),
|
|
26455
26473
|
memoryMib: z62.int(),
|
|
@@ -26489,7 +26507,8 @@ async function readDatabaseCopy(db, path) {
|
|
|
26489
26507
|
await writeConsistentCopy(db, path);
|
|
26490
26508
|
const copy = new Database2(path, { readonly: true });
|
|
26491
26509
|
try {
|
|
26492
|
-
const imps = copy.query(`SELECT id, name, image_id AS imageId, state, vcpus,
|
|
26510
|
+
const imps = copy.query(`SELECT id, name, image_id AS imageId, image_digest AS imageDigest, state, vcpus,
|
|
26511
|
+
memory_mib AS memoryMib,
|
|
26493
26512
|
max_memory_mib AS maxMemoryMib, http_port AS httpPort, egress_policy AS egressPolicy,
|
|
26494
26513
|
egress_allow AS egressAllow, disk_bytes AS diskBytes,
|
|
26495
26514
|
identity_reset_pending AS identityResetPending, backup
|
|
@@ -27078,17 +27097,26 @@ async function updateImpState(db, id, change, expected) {
|
|
|
27078
27097
|
if (change.firecrackerVersion !== undefined) {
|
|
27079
27098
|
values.firecracker_version = change.firecrackerVersion;
|
|
27080
27099
|
}
|
|
27081
|
-
|
|
27082
|
-
|
|
27083
|
-
if (
|
|
27084
|
-
|
|
27100
|
+
const row = await db.transaction().execute(async (trx) => {
|
|
27101
|
+
const current = await trx.selectFrom("imps").select(["state", "pid"]).where("id", "=", id).executeTakeFirst();
|
|
27102
|
+
if (current === undefined) {
|
|
27103
|
+
return null;
|
|
27104
|
+
}
|
|
27105
|
+
if (expected !== undefined && (current.state !== expected.state || current.pid !== expected.pid)) {
|
|
27106
|
+
return null;
|
|
27107
|
+
}
|
|
27108
|
+
if (change.state !== current.state) {
|
|
27109
|
+
requireTransition(current.state, change.state, `move to ${change.state}`);
|
|
27085
27110
|
}
|
|
27086
|
-
|
|
27087
|
-
|
|
27111
|
+
return trx.updateTable("imps").set({ ...values, ...buildAwakeValues(change), ...buildBootCauseValues(change) }).where("id", "=", id).returningAll().executeTakeFirstOrThrow();
|
|
27112
|
+
});
|
|
27113
|
+
if (row !== null) {
|
|
27114
|
+
return emitChange(db, toImpRecord(row), change);
|
|
27088
27115
|
}
|
|
27089
|
-
|
|
27090
|
-
|
|
27091
|
-
|
|
27116
|
+
if (expected === undefined) {
|
|
27117
|
+
throw buildNotFoundError("imp", id);
|
|
27118
|
+
}
|
|
27119
|
+
return;
|
|
27092
27120
|
}
|
|
27093
27121
|
function buildAwakeValues(change) {
|
|
27094
27122
|
const now = Date.now();
|
|
@@ -27686,9 +27714,6 @@ async function updateLockedImpState(db, imp, change) {
|
|
|
27686
27714
|
if (!isLockedImp(record)) {
|
|
27687
27715
|
throw new Error(`imp ${record.id}: a state write needs the imp's lock`);
|
|
27688
27716
|
}
|
|
27689
|
-
if (change.state !== imp.state) {
|
|
27690
|
-
requireTransition(imp.state, change.state, `move to ${change.state}`);
|
|
27691
|
-
}
|
|
27692
27717
|
const updated = await updateImpState(db, imp.id, change);
|
|
27693
27718
|
return toLockedImp(imp, updated);
|
|
27694
27719
|
}
|
|
@@ -27876,12 +27901,7 @@ function createCheckpointService(deps) {
|
|
|
27876
27901
|
throw buildInvalidStateError(imp.state, ["running", "sleeping", "stopped", "error"], "fork");
|
|
27877
27902
|
}
|
|
27878
27903
|
const api = await deps.imps.toApi(imp);
|
|
27879
|
-
return {
|
|
27880
|
-
imp: api,
|
|
27881
|
-
policy,
|
|
27882
|
-
isIdentityResetPending: imp.isIdentityResetPending,
|
|
27883
|
-
isBackedUp: imp.isBackedUp
|
|
27884
|
-
};
|
|
27904
|
+
return { imp: api, policy, isBackedUp: imp.isBackedUp, imageDigest: imp.imageDigest };
|
|
27885
27905
|
});
|
|
27886
27906
|
const made = { forkId: null, isSourceChanged: false };
|
|
27887
27907
|
const requireSameSource = (imp) => {
|
|
@@ -27917,6 +27937,7 @@ function createCheckpointService(deps) {
|
|
|
27917
27937
|
const fork = await deps.imps.createImp({
|
|
27918
27938
|
name: input.name,
|
|
27919
27939
|
image: source.imp.image,
|
|
27940
|
+
imageDigest: source.imageDigest,
|
|
27920
27941
|
vcpus: source.imp.vcpus,
|
|
27921
27942
|
memoryMib: source.imp.memoryMib,
|
|
27922
27943
|
maxMemoryMib: source.imp.maxMemoryMib,
|
|
@@ -27925,7 +27946,7 @@ function createCheckpointService(deps) {
|
|
|
27925
27946
|
cpuLimit: source.imp.cpu.limit,
|
|
27926
27947
|
cpuWeight: source.imp.cpu.weight
|
|
27927
27948
|
},
|
|
27928
|
-
isIdentityResetPending:
|
|
27949
|
+
isIdentityResetPending: true,
|
|
27929
27950
|
isBackedUp: source.isBackedUp,
|
|
27930
27951
|
prepareDisk: createForkDisk
|
|
27931
27952
|
});
|
|
@@ -28030,6 +28051,7 @@ var ManifestImpSchema = z64.object({
|
|
|
28030
28051
|
id: z64.string(),
|
|
28031
28052
|
name: z64.string(),
|
|
28032
28053
|
imageDigest: z64.string(),
|
|
28054
|
+
clonedDigest: z64.string().optional(),
|
|
28033
28055
|
vcpus: z64.int(),
|
|
28034
28056
|
memoryMib: z64.int(),
|
|
28035
28057
|
maxMemoryMib: z64.int().nullable().optional(),
|
|
@@ -28112,7 +28134,7 @@ function createEventBus() {
|
|
|
28112
28134
|
}
|
|
28113
28135
|
|
|
28114
28136
|
// packages/daemon/src/platform/db/run-migrations.ts
|
|
28115
|
-
import { sql as
|
|
28137
|
+
import { sql as sql13 } from "kysely";
|
|
28116
28138
|
import { Migrator } from "kysely/migration";
|
|
28117
28139
|
|
|
28118
28140
|
// packages/daemon/src/platform/db/migrations/migration-001-create-initial-schema.ts
|
|
@@ -28764,6 +28786,17 @@ var MIGRATION_043_MERGE_SECRET_KINDS = {
|
|
|
28764
28786
|
}
|
|
28765
28787
|
};
|
|
28766
28788
|
|
|
28789
|
+
// packages/daemon/src/platform/db/migrations/migration-044-add-imp-image-digests.ts
|
|
28790
|
+
import { sql as sql12 } from "kysely";
|
|
28791
|
+
var MIGRATION_044_ADD_IMP_IMAGE_DIGESTS = {
|
|
28792
|
+
async up(db) {
|
|
28793
|
+
await db.schema.alterTable("imps").addColumn("image_digest", "text", (column) => column.notNull().defaultTo("")).execute();
|
|
28794
|
+
await sql12`UPDATE imps SET image_digest = (
|
|
28795
|
+
SELECT images.digest FROM images WHERE images.id = imps.image_id
|
|
28796
|
+
)`.execute(db);
|
|
28797
|
+
}
|
|
28798
|
+
};
|
|
28799
|
+
|
|
28767
28800
|
// packages/daemon/src/platform/db/run-migrations.ts
|
|
28768
28801
|
var MIGRATIONS = {
|
|
28769
28802
|
"001_create_initial_schema": MIGRATION_001_CREATE_INITIAL_SCHEMA,
|
|
@@ -28808,7 +28841,8 @@ var MIGRATIONS = {
|
|
|
28808
28841
|
"040_add_image_received_digests": MIGRATION_040_ADD_IMAGE_RECEIVED_DIGESTS,
|
|
28809
28842
|
"041_add_move_send_steps": MIGRATION_041_ADD_MOVE_SEND_STEPS,
|
|
28810
28843
|
"042_add_secret_env": MIGRATION_042_ADD_SECRET_ENV,
|
|
28811
|
-
"043_merge_secret_kinds": MIGRATION_043_MERGE_SECRET_KINDS
|
|
28844
|
+
"043_merge_secret_kinds": MIGRATION_043_MERGE_SECRET_KINDS,
|
|
28845
|
+
"044_add_imp_image_digests": MIGRATION_044_ADD_IMP_IMAGE_DIGESTS
|
|
28812
28846
|
};
|
|
28813
28847
|
var PROVIDER = {
|
|
28814
28848
|
getMigrations: () => Promise.resolve(MIGRATIONS)
|
|
@@ -28820,13 +28854,13 @@ async function runMigrations(db) {
|
|
|
28820
28854
|
requireMigrated(result);
|
|
28821
28855
|
}
|
|
28822
28856
|
async function requireKnownMigrations(db) {
|
|
28823
|
-
const table = await
|
|
28857
|
+
const table = await sql13`
|
|
28824
28858
|
SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'kysely_migration'
|
|
28825
28859
|
`.execute(db);
|
|
28826
28860
|
if (table.rows.length === 0) {
|
|
28827
28861
|
return;
|
|
28828
28862
|
}
|
|
28829
|
-
const executed = await
|
|
28863
|
+
const executed = await sql13`SELECT name FROM kysely_migration`.execute(db);
|
|
28830
28864
|
const unknown = executed.rows.map((row) => row.name).filter((name) => !Object.hasOwn(MIGRATIONS, name));
|
|
28831
28865
|
if (unknown.length === 0) {
|
|
28832
28866
|
return;
|
|
@@ -29068,8 +29102,11 @@ async function createRestoredImp(deps, options) {
|
|
|
29068
29102
|
await writeRestoredFile(imp.disk, disk, imp);
|
|
29069
29103
|
};
|
|
29070
29104
|
const networks = await deps.networks.writeMissingNetworks(imp.networks);
|
|
29105
|
+
const idHolder = await readImpById(deps.db, imp.id);
|
|
29071
29106
|
try {
|
|
29072
29107
|
return await deps.imps.createImp({
|
|
29108
|
+
...idHolder === undefined && { id: imp.id },
|
|
29109
|
+
...imp.clonedDigest !== undefined && { imageDigest: imp.clonedDigest },
|
|
29073
29110
|
name: target.name,
|
|
29074
29111
|
networkIds: networks.ids,
|
|
29075
29112
|
image: target.image,
|
|
@@ -29113,6 +29150,7 @@ var CHECK_EVERY_MS = 7 * DAY_MS;
|
|
|
29113
29150
|
var CHECK_SUBSET = "5%";
|
|
29114
29151
|
var PRUNE_LOCK_TRIES = 6;
|
|
29115
29152
|
var RETRY_FIRST_MS = 5 * 60 * 1000;
|
|
29153
|
+
var BUSY_REASON = "busy with another job";
|
|
29116
29154
|
var BackupStateSchema = z65.object({
|
|
29117
29155
|
lastRunAt: z65.coerce.date().nullable().default(null),
|
|
29118
29156
|
lastPruneAt: z65.coerce.date().nullable().default(null),
|
|
@@ -29194,6 +29232,7 @@ function createBackupService(deps) {
|
|
|
29194
29232
|
id: imp.id,
|
|
29195
29233
|
name: imp.name,
|
|
29196
29234
|
imageDigest: digests.get(imp.imageId) ?? "",
|
|
29235
|
+
clonedDigest: imp.imageDigest,
|
|
29197
29236
|
vcpus: imp.vcpus,
|
|
29198
29237
|
memoryMib: imp.memoryMib,
|
|
29199
29238
|
maxMemoryMib: imp.maxMemoryMib,
|
|
@@ -29255,8 +29294,15 @@ function createBackupService(deps) {
|
|
|
29255
29294
|
continue;
|
|
29256
29295
|
}
|
|
29257
29296
|
try {
|
|
29258
|
-
const made = await deps.imps.
|
|
29259
|
-
|
|
29297
|
+
const made = await deps.imps.tryLockImpId(imp.id, (locked) => locked === undefined ? Promise.reject(new Error("removed since the database copy")) : createCopy(locked, runId));
|
|
29298
|
+
if (!made.ran) {
|
|
29299
|
+
skipped.push({ name: imp.name, reason: BUSY_REASON });
|
|
29300
|
+
getImpdLogger("backup").with({ imp: imp.name }).info("left out: {reason}", {
|
|
29301
|
+
reason: BUSY_REASON
|
|
29302
|
+
});
|
|
29303
|
+
continue;
|
|
29304
|
+
}
|
|
29305
|
+
copies.set(imp.id, made.value);
|
|
29260
29306
|
} catch (error) {
|
|
29261
29307
|
skipped.push({ name: imp.name, reason: getErrorMessage(error) });
|
|
29262
29308
|
getImpdLogger("backup").with({ imp: imp.name }).warning("left out: {error}", { error: getErrorMessage(error) });
|
|
@@ -37501,6 +37547,31 @@ function readTapOwner(tap) {
|
|
|
37501
37547
|
}
|
|
37502
37548
|
}
|
|
37503
37549
|
|
|
37550
|
+
// packages/daemon/src/imp-ops/governor/build-deferred-admission.ts
|
|
37551
|
+
import invariant4 from "tiny-invariant";
|
|
37552
|
+
function buildDeferredAdmission() {
|
|
37553
|
+
const holder = { governor: null };
|
|
37554
|
+
const requireGovernor = () => {
|
|
37555
|
+
invariant4(holder.governor, "RAM admission ran before the RAM governor was attached");
|
|
37556
|
+
return holder.governor;
|
|
37557
|
+
};
|
|
37558
|
+
return {
|
|
37559
|
+
admission: {
|
|
37560
|
+
admit: (request) => requireGovernor().admit(request),
|
|
37561
|
+
release: (id) => {
|
|
37562
|
+
requireGovernor().release(id);
|
|
37563
|
+
}
|
|
37564
|
+
},
|
|
37565
|
+
admitGrow: (request) => requireGovernor().admitGrow(request),
|
|
37566
|
+
releaseGrow: (id) => {
|
|
37567
|
+
requireGovernor().releaseGrow(id);
|
|
37568
|
+
},
|
|
37569
|
+
attach: (governor) => {
|
|
37570
|
+
holder.governor = governor;
|
|
37571
|
+
}
|
|
37572
|
+
};
|
|
37573
|
+
}
|
|
37574
|
+
|
|
37504
37575
|
// packages/daemon/src/platform/process/semaphore.ts
|
|
37505
37576
|
function createSemaphore(limit) {
|
|
37506
37577
|
const waiters = [];
|
|
@@ -37528,6 +37599,14 @@ function createSemaphore(limit) {
|
|
|
37528
37599
|
};
|
|
37529
37600
|
}
|
|
37530
37601
|
|
|
37602
|
+
// packages/daemon/src/imp-ops/governor/build-budgets.ts
|
|
37603
|
+
function buildBudgets(budgetMib, pool) {
|
|
37604
|
+
return {
|
|
37605
|
+
ramMib: budgetMib,
|
|
37606
|
+
ordinaryMib: pool === null || pool.totalMib === 0 ? null : Math.max(0, budgetMib - pool.totalMib)
|
|
37607
|
+
};
|
|
37608
|
+
}
|
|
37609
|
+
|
|
37531
37610
|
// packages/daemon/src/imp-ops/governor/build-huge-pool-admission.ts
|
|
37532
37611
|
function buildHugePoolAdmission(input) {
|
|
37533
37612
|
const growthMib = input.awake.reduce((sum, guest) => sum + Math.max(0, guest.hugePagesMib - guest.heldMib), 0);
|
|
@@ -37546,6 +37625,66 @@ function buildHugePoolAdmission(input) {
|
|
|
37546
37625
|
};
|
|
37547
37626
|
}
|
|
37548
37627
|
|
|
37628
|
+
// packages/daemon/src/imp-ops/governor/make-check-huge-pool.ts
|
|
37629
|
+
function makeCheckHugePool(deps) {
|
|
37630
|
+
const pendingHugePages = deps.pendingHugePages;
|
|
37631
|
+
return async (request, hugePagesMib) => {
|
|
37632
|
+
const listed = await deps.listAwake();
|
|
37633
|
+
const awake = listed.filter((imp) => imp.id !== request.id);
|
|
37634
|
+
for (const imp of awake) {
|
|
37635
|
+
pendingHugePages.delete(imp.id);
|
|
37636
|
+
}
|
|
37637
|
+
const onPages = awake.filter((imp) => imp.hugePagesMib > 0);
|
|
37638
|
+
const held = await Promise.all(onPages.map((imp) => deps.readHugetlbMib?.(imp.pid, imp.apiSocket) ?? Promise.resolve(0)));
|
|
37639
|
+
const pool = deps.readHugePool?.() ?? null;
|
|
37640
|
+
const poolAdmission = buildHugePoolAdmission({
|
|
37641
|
+
pool,
|
|
37642
|
+
awake: onPages.map((imp, index) => ({
|
|
37643
|
+
hugePagesMib: imp.hugePagesMib,
|
|
37644
|
+
heldMib: held[index] ?? 0
|
|
37645
|
+
})),
|
|
37646
|
+
pendingMib: [...pendingHugePages].filter(([id]) => id !== request.id).map(([, mib]) => mib),
|
|
37647
|
+
requestedMib: hugePagesMib
|
|
37648
|
+
});
|
|
37649
|
+
if (poolAdmission.admitted) {
|
|
37650
|
+
return;
|
|
37651
|
+
}
|
|
37652
|
+
if (request.name !== null) {
|
|
37653
|
+
deps.onRefused({
|
|
37654
|
+
name: request.name,
|
|
37655
|
+
usedMib: poolAdmission.committedMib,
|
|
37656
|
+
reserveMib: hugePagesMib,
|
|
37657
|
+
neededMib: poolAdmission.neededMib
|
|
37658
|
+
});
|
|
37659
|
+
}
|
|
37660
|
+
throw buildHugePoolError({
|
|
37661
|
+
totalMib: poolAdmission.totalMib,
|
|
37662
|
+
committedMib: poolAdmission.committedMib,
|
|
37663
|
+
requestedMib: hugePagesMib
|
|
37664
|
+
});
|
|
37665
|
+
};
|
|
37666
|
+
}
|
|
37667
|
+
|
|
37668
|
+
// packages/daemon/src/imp-ops/governor/pick-shortfall.ts
|
|
37669
|
+
function pickShortfall(used, budgets, requestedMib) {
|
|
37670
|
+
const ram = {
|
|
37671
|
+
budget: "ram",
|
|
37672
|
+
budgetMib: budgets.ramMib,
|
|
37673
|
+
usedMib: used.ramMib,
|
|
37674
|
+
missingMib: used.ramMib + requestedMib - budgets.ramMib
|
|
37675
|
+
};
|
|
37676
|
+
if (budgets.ordinaryMib === null) {
|
|
37677
|
+
return ram;
|
|
37678
|
+
}
|
|
37679
|
+
const ordinaryMissingMib = used.ordinaryMib + requestedMib - budgets.ordinaryMib;
|
|
37680
|
+
return ordinaryMissingMib > ram.missingMib ? {
|
|
37681
|
+
budget: "ordinary",
|
|
37682
|
+
budgetMib: budgets.ordinaryMib,
|
|
37683
|
+
usedMib: used.ordinaryMib,
|
|
37684
|
+
missingMib: ordinaryMissingMib
|
|
37685
|
+
} : ram;
|
|
37686
|
+
}
|
|
37687
|
+
|
|
37549
37688
|
// packages/daemon/src/imp-ops/governor/pick-sleep-victims.ts
|
|
37550
37689
|
function pickSleepVictims(candidates, needMib) {
|
|
37551
37690
|
if (needMib <= 0) {
|
|
@@ -37568,6 +37707,7 @@ function pickSleepVictims(candidates, needMib) {
|
|
|
37568
37707
|
var RESERVATION_TTL_MS = 20000;
|
|
37569
37708
|
var ENFORCE_INTERVAL_MS = 5000;
|
|
37570
37709
|
var ENFORCE_TRIGGER = "RAM over budget";
|
|
37710
|
+
var ORDINARY_TRIGGER = "RAM outside the huge page pool";
|
|
37571
37711
|
function createRamGovernor(deps) {
|
|
37572
37712
|
const admission = createSemaphore(1);
|
|
37573
37713
|
const reservations = new Map;
|
|
@@ -37595,6 +37735,7 @@ function createRamGovernor(deps) {
|
|
|
37595
37735
|
});
|
|
37596
37736
|
}
|
|
37597
37737
|
};
|
|
37738
|
+
const readBudgets = () => buildBudgets(deps.budgetMib, deps.readHugePool?.() ?? null);
|
|
37598
37739
|
const recordAdmission = (request, usedMib) => {
|
|
37599
37740
|
reservations.set(request.id, {
|
|
37600
37741
|
mib: request.reserveMib,
|
|
@@ -37616,72 +37757,66 @@ function createRamGovernor(deps) {
|
|
|
37616
37757
|
}
|
|
37617
37758
|
return reservation.mib;
|
|
37618
37759
|
};
|
|
37619
|
-
const checkHugePool =
|
|
37620
|
-
|
|
37621
|
-
|
|
37622
|
-
|
|
37623
|
-
|
|
37624
|
-
|
|
37625
|
-
const onPages = awake.filter((imp) => imp.hugePagesMib > 0);
|
|
37626
|
-
const held = await Promise.all(onPages.map((imp) => deps.readHugetlbMib?.(imp.pid, imp.apiSocket) ?? Promise.resolve(0)));
|
|
37627
|
-
const pool = deps.readHugePool?.() ?? null;
|
|
37628
|
-
const poolAdmission = buildHugePoolAdmission({
|
|
37629
|
-
pool,
|
|
37630
|
-
awake: onPages.map((imp, index) => ({
|
|
37631
|
-
hugePagesMib: imp.hugePagesMib,
|
|
37632
|
-
heldMib: held[index] ?? 0
|
|
37633
|
-
})),
|
|
37634
|
-
pendingMib: [...pendingHugePages].filter(([id]) => id !== request.id).map(([, mib]) => mib),
|
|
37635
|
-
requestedMib: hugePagesMib
|
|
37636
|
-
});
|
|
37637
|
-
if (poolAdmission.admitted) {
|
|
37638
|
-
return;
|
|
37639
|
-
}
|
|
37640
|
-
if (request.name !== null) {
|
|
37760
|
+
const checkHugePool = makeCheckHugePool({
|
|
37761
|
+
listAwake: deps.listAwake,
|
|
37762
|
+
...deps.readHugePool !== undefined && { readHugePool: deps.readHugePool },
|
|
37763
|
+
...deps.readHugetlbMib !== undefined && { readHugetlbMib: deps.readHugetlbMib },
|
|
37764
|
+
pendingHugePages,
|
|
37765
|
+
onRefused: (refusal) => {
|
|
37641
37766
|
emitDecision({
|
|
37642
37767
|
decision: "refused",
|
|
37643
|
-
name: request.name,
|
|
37644
37768
|
trigger: "huge page pool",
|
|
37645
|
-
|
|
37646
|
-
|
|
37647
|
-
neededMib: poolAdmission.neededMib,
|
|
37648
|
-
protectedCount: 0
|
|
37769
|
+
protectedCount: 0,
|
|
37770
|
+
...refusal
|
|
37649
37771
|
});
|
|
37650
37772
|
}
|
|
37651
|
-
|
|
37652
|
-
|
|
37653
|
-
committedMib: poolAdmission.committedMib,
|
|
37654
|
-
requestedMib: hugePagesMib
|
|
37655
|
-
});
|
|
37656
|
-
};
|
|
37657
|
-
const readEffectiveUsage = async (excludeId, readVmMib = deps.readRamMib) => {
|
|
37773
|
+
});
|
|
37774
|
+
const readEffectiveUsage = async (excludeId, readVm = deps.readVmRam) => {
|
|
37658
37775
|
const listed = await deps.listAwake();
|
|
37659
37776
|
const awake = listed.filter((imp) => imp.id !== excludeId);
|
|
37660
37777
|
const byImp = new Map;
|
|
37661
37778
|
const lockedMib = deps.readLockedMib?.() ?? 0;
|
|
37662
37779
|
let usedMib = lockedMib;
|
|
37663
37780
|
let effectiveMib = lockedMib;
|
|
37664
|
-
|
|
37781
|
+
let ordinaryUsedMib = lockedMib;
|
|
37782
|
+
let ordinaryEffectiveMib = lockedMib;
|
|
37783
|
+
const readings = await Promise.all(awake.map((imp) => readVm(imp.pid, imp.apiSocket)));
|
|
37665
37784
|
for (const [index, imp] of awake.entries()) {
|
|
37666
|
-
const
|
|
37785
|
+
const reading = readings[index] ?? null;
|
|
37786
|
+
const measured = reading?.ramMib ?? 0;
|
|
37667
37787
|
const effective = Math.max(measured, readReservation(imp.id));
|
|
37788
|
+
const ordinary = imp.hugePagesMib > 0 ? Math.max(0, measured - (reading?.hugetlbMib ?? 0)) : effective;
|
|
37668
37789
|
byImp.set(imp.id, effective);
|
|
37669
37790
|
usedMib += measured;
|
|
37670
37791
|
effectiveMib += effective;
|
|
37792
|
+
ordinaryUsedMib += imp.hugePagesMib > 0 ? ordinary : measured;
|
|
37793
|
+
ordinaryEffectiveMib += ordinary;
|
|
37671
37794
|
}
|
|
37672
37795
|
for (const id of reservations.keys()) {
|
|
37673
37796
|
if (id !== excludeId && !byImp.has(id)) {
|
|
37674
37797
|
effectiveMib += readReservation(id);
|
|
37798
|
+
if (!pendingHugePages.has(id)) {
|
|
37799
|
+
ordinaryEffectiveMib += readReservation(id);
|
|
37800
|
+
}
|
|
37675
37801
|
}
|
|
37676
37802
|
}
|
|
37677
37803
|
const headroom = deps.readHeadroomMib?.(awake.map((imp) => imp.pid)) ?? Promise.resolve(0);
|
|
37678
37804
|
const headroomMib = await headroom;
|
|
37679
37805
|
effectiveMib += headroomMib;
|
|
37680
|
-
|
|
37806
|
+
ordinaryEffectiveMib += headroomMib;
|
|
37807
|
+
return {
|
|
37808
|
+
awake,
|
|
37809
|
+
byImp,
|
|
37810
|
+
usedMib,
|
|
37811
|
+
effectiveMib,
|
|
37812
|
+
ordinaryUsedMib,
|
|
37813
|
+
ordinaryEffectiveMib,
|
|
37814
|
+
headroomMib
|
|
37815
|
+
};
|
|
37681
37816
|
};
|
|
37682
|
-
const readFreeRoom = async (excludeId,
|
|
37817
|
+
const readFreeRoom = async (excludeId, findShortfall) => {
|
|
37683
37818
|
const usage = await readEffectiveUsage(excludeId);
|
|
37684
|
-
const missingMib =
|
|
37819
|
+
const missingMib = findShortfall(usage).missingMib;
|
|
37685
37820
|
return {
|
|
37686
37821
|
fits: missingMib <= 0,
|
|
37687
37822
|
slept: 0,
|
|
@@ -37702,7 +37837,10 @@ function createRamGovernor(deps) {
|
|
|
37702
37837
|
let diskFull = false;
|
|
37703
37838
|
for (;; ) {
|
|
37704
37839
|
const usage = await readEffectiveUsage(excludeId);
|
|
37705
|
-
const
|
|
37840
|
+
const shortfall = options.findShortfall(usage);
|
|
37841
|
+
const missingMib = shortfall.missingMib;
|
|
37842
|
+
const isOrdinary = shortfall.budget === "ordinary";
|
|
37843
|
+
const sleepable = usage.awake.filter((imp) => !isOrdinary || imp.hugePagesMib === 0);
|
|
37706
37844
|
if (missingMib <= 0) {
|
|
37707
37845
|
return {
|
|
37708
37846
|
fits: true,
|
|
@@ -37725,7 +37863,7 @@ function createRamGovernor(deps) {
|
|
|
37725
37863
|
continue;
|
|
37726
37864
|
}
|
|
37727
37865
|
const time = now();
|
|
37728
|
-
const candidates =
|
|
37866
|
+
const candidates = sleepable.map((imp) => ({
|
|
37729
37867
|
id: imp.id,
|
|
37730
37868
|
name: imp.name,
|
|
37731
37869
|
ramMib: usage.byImp.get(imp.id) ?? 0,
|
|
@@ -37768,29 +37906,39 @@ function createRamGovernor(deps) {
|
|
|
37768
37906
|
};
|
|
37769
37907
|
return {
|
|
37770
37908
|
admit: (request) => admission.run(async () => {
|
|
37771
|
-
|
|
37909
|
+
const hugePagesMib = request.hugePagesMib ?? 0;
|
|
37910
|
+
const hostBudgets = readBudgets();
|
|
37911
|
+
const budgets = hugePagesMib > 0 ? { ...hostBudgets, ordinaryMib: null } : hostBudgets;
|
|
37912
|
+
const whole = pickShortfall({ ramMib: 0, ordinaryMib: 0 }, budgets, request.memoryMib);
|
|
37913
|
+
if (whole.missingMib > 0) {
|
|
37772
37914
|
const usage = await readEffectiveUsage(request.id);
|
|
37773
|
-
|
|
37774
|
-
|
|
37775
|
-
|
|
37915
|
+
const isOrdinary = whole.budget === "ordinary";
|
|
37916
|
+
const usedMib = isOrdinary ? usage.ordinaryEffectiveMib : usage.effectiveMib;
|
|
37917
|
+
emitAdmission(request, "refused", usedMib, {
|
|
37918
|
+
neededMib: buildNeededMib(usedMib, request.memoryMib, whole.budgetMib),
|
|
37919
|
+
protectedCount: 0,
|
|
37920
|
+
...isOrdinary && { trigger: ORDINARY_TRIGGER }
|
|
37921
|
+
});
|
|
37922
|
+
throw buildImpOverBudgetError(whole.budget, {
|
|
37923
|
+
budgetMib: whole.budgetMib,
|
|
37924
|
+
usedMib,
|
|
37925
|
+
requestedMib: request.memoryMib
|
|
37776
37926
|
});
|
|
37777
|
-
throw buildImpOverBudgetError(deps.budgetMib, usage.effectiveMib, request.memoryMib);
|
|
37778
37927
|
}
|
|
37779
|
-
const hugePagesMib = request.hugePagesMib ?? 0;
|
|
37780
37928
|
if (hugePagesMib > 0) {
|
|
37781
37929
|
await checkHugePool(request, hugePagesMib);
|
|
37782
37930
|
}
|
|
37783
|
-
const bound = await readEffectiveUsage(request.id, deps.
|
|
37784
|
-
if (bound
|
|
37931
|
+
const bound = await readEffectiveUsage(request.id, deps.readResidentRam);
|
|
37932
|
+
if (pickEffectiveShortfall(bound, budgets, request.reserveMib).missingMib <= 0) {
|
|
37785
37933
|
recordAdmission(request, bound.effectiveMib);
|
|
37786
37934
|
return;
|
|
37787
37935
|
}
|
|
37788
|
-
const
|
|
37789
|
-
const room = request.maySleepImps === false ? await readFreeRoom(request.id,
|
|
37936
|
+
const findReserveShortfall = (usage) => pickEffectiveShortfall(usage, budgets, request.reserveMib);
|
|
37937
|
+
const room = request.maySleepImps === false ? await readFreeRoom(request.id, findReserveShortfall) : await makeRoom({
|
|
37790
37938
|
excludeId: request.id,
|
|
37791
37939
|
reason: `to make room for ${request.name ?? request.id}`,
|
|
37792
37940
|
whenShort: "giveUp",
|
|
37793
|
-
|
|
37941
|
+
findShortfall: findReserveShortfall
|
|
37794
37942
|
});
|
|
37795
37943
|
const diskFull = room.diskFull ? deps.readDiskFullError?.() ?? null : null;
|
|
37796
37944
|
if (!room.fits && diskFull !== null) {
|
|
@@ -37798,16 +37946,20 @@ function createRamGovernor(deps) {
|
|
|
37798
37946
|
}
|
|
37799
37947
|
if (!room.fits) {
|
|
37800
37948
|
const usage = await readEffectiveUsage(request.id);
|
|
37801
|
-
|
|
37802
|
-
|
|
37803
|
-
|
|
37949
|
+
const shortfall = findReserveShortfall(usage);
|
|
37950
|
+
const isOrdinary = shortfall.budget === "ordinary";
|
|
37951
|
+
emitAdmission(request, "refused", shortfall.usedMib, {
|
|
37952
|
+
neededMib: buildNeededMib(shortfall.usedMib, request.reserveMib, shortfall.budgetMib),
|
|
37953
|
+
protectedCount: room.protected.length,
|
|
37954
|
+
...isOrdinary && { trigger: ORDINARY_TRIGGER }
|
|
37804
37955
|
});
|
|
37805
|
-
|
|
37806
|
-
budgetMib:
|
|
37807
|
-
usedMib:
|
|
37956
|
+
const refusal = {
|
|
37957
|
+
budgetMib: shortfall.budgetMib,
|
|
37958
|
+
usedMib: shortfall.usedMib,
|
|
37808
37959
|
requestedMib: request.reserveMib,
|
|
37809
37960
|
protected: room.protected
|
|
37810
|
-
}
|
|
37961
|
+
};
|
|
37962
|
+
throw isOrdinary ? buildOrdinaryBudgetError(refusal) : buildRamBudgetError(refusal);
|
|
37811
37963
|
}
|
|
37812
37964
|
recordAdmission(request, room.effectiveMib);
|
|
37813
37965
|
}),
|
|
@@ -37820,23 +37972,25 @@ function createRamGovernor(deps) {
|
|
|
37820
37972
|
if (!listed.some((imp) => imp.id === request.id)) {
|
|
37821
37973
|
return false;
|
|
37822
37974
|
}
|
|
37975
|
+
const budgets = readBudgets();
|
|
37823
37976
|
const room = await makeRoom({
|
|
37824
37977
|
excludeId: null,
|
|
37825
37978
|
reason: `to grow ${request.name}`,
|
|
37826
37979
|
whenShort: "giveUp",
|
|
37827
|
-
|
|
37980
|
+
findShortfall: (usage) => pickEffectiveShortfall(usage, budgets, request.mib),
|
|
37828
37981
|
protectedId: request.id
|
|
37829
37982
|
});
|
|
37830
37983
|
const usage = await readEffectiveUsage(null);
|
|
37984
|
+
const shortfall = pickEffectiveShortfall(usage, budgets, request.mib);
|
|
37831
37985
|
const fits = room.fits && usage.awake.some((imp) => imp.id === request.id);
|
|
37832
37986
|
emitDecision({
|
|
37833
37987
|
decision: fits ? "admitted" : "refused",
|
|
37834
37988
|
name: request.name,
|
|
37835
37989
|
trigger: "grow",
|
|
37836
|
-
usedMib:
|
|
37990
|
+
usedMib: shortfall.usedMib,
|
|
37837
37991
|
reserveMib: request.mib,
|
|
37838
37992
|
...!fits && {
|
|
37839
|
-
neededMib:
|
|
37993
|
+
neededMib: Math.max(0, shortfall.missingMib),
|
|
37840
37994
|
protectedCount: room.protected.length
|
|
37841
37995
|
}
|
|
37842
37996
|
});
|
|
@@ -37871,11 +38025,12 @@ function createRamGovernor(deps) {
|
|
|
37871
38025
|
};
|
|
37872
38026
|
},
|
|
37873
38027
|
enforce: () => admission.run(async () => {
|
|
38028
|
+
const budgets = readBudgets();
|
|
37874
38029
|
const room = await makeRoom({
|
|
37875
38030
|
excludeId: null,
|
|
37876
38031
|
reason: ENFORCE_TRIGGER,
|
|
37877
38032
|
whenShort: "sleepAll",
|
|
37878
|
-
|
|
38033
|
+
findShortfall: (usage) => pickShortfall({ ramMib: usage.usedMib, ordinaryMib: usage.ordinaryUsedMib }, budgets, 0)
|
|
37879
38034
|
});
|
|
37880
38035
|
const over = `over budget by ${String(room.missingMib)} MiB`;
|
|
37881
38036
|
if (room.fits) {
|
|
@@ -37893,6 +38048,9 @@ function createRamGovernor(deps) {
|
|
|
37893
38048
|
})
|
|
37894
38049
|
};
|
|
37895
38050
|
}
|
|
38051
|
+
function pickEffectiveShortfall(usage, budgets, requestedMib) {
|
|
38052
|
+
return pickShortfall({ ramMib: usage.effectiveMib, ordinaryMib: usage.ordinaryEffectiveMib }, budgets, requestedMib);
|
|
38053
|
+
}
|
|
37896
38054
|
|
|
37897
38055
|
// packages/daemon/src/vm/watchdog/agent-watchdog.ts
|
|
37898
38056
|
var HOUR_MS = 3600000;
|
|
@@ -38249,6 +38407,7 @@ async function createImpRecord(context, id, input, image) {
|
|
|
38249
38407
|
id,
|
|
38250
38408
|
name,
|
|
38251
38409
|
imageId: image.id,
|
|
38410
|
+
imageDigest: input.imageDigest ?? image.digest,
|
|
38252
38411
|
vcpus: input.vcpus ?? context.config.defaultVcpus,
|
|
38253
38412
|
memoryMib: input.memoryMib ?? context.config.defaultMemoryMib,
|
|
38254
38413
|
...input.maxMemoryMib !== undefined && { maxMemoryMib: input.maxMemoryMib },
|
|
@@ -38316,7 +38475,7 @@ function createImpCommands(parts) {
|
|
|
38316
38475
|
await context.egress.releaseSlot(imp.slot);
|
|
38317
38476
|
const checkpoints = await listCheckpoints(context.db, imp.id);
|
|
38318
38477
|
await removeImpFiles(context, imp.id, checkpoints.map((checkpoint) => checkpoint.id));
|
|
38319
|
-
context.admission
|
|
38478
|
+
context.admission.release(imp.id);
|
|
38320
38479
|
await removeImp(context.db, imp.id);
|
|
38321
38480
|
};
|
|
38322
38481
|
return {
|
|
@@ -39097,7 +39256,7 @@ function createBootTemplates(deps) {
|
|
|
39097
39256
|
return writeTemplate(shape, key, work, buildId);
|
|
39098
39257
|
};
|
|
39099
39258
|
try {
|
|
39100
|
-
await deps.admission
|
|
39259
|
+
await deps.admission.admit({
|
|
39101
39260
|
id,
|
|
39102
39261
|
name: null,
|
|
39103
39262
|
reserveMib: Math.ceil(shape.memoryMib * deps.bootReservePercent / 100),
|
|
@@ -39112,7 +39271,7 @@ function createBootTemplates(deps) {
|
|
|
39112
39271
|
}
|
|
39113
39272
|
throw new BuildRefusedError(getErrorMessage(error), { cause: error });
|
|
39114
39273
|
} finally {
|
|
39115
|
-
deps.admission
|
|
39274
|
+
deps.admission.release(id);
|
|
39116
39275
|
rmSync15(work, { recursive: true, force: true });
|
|
39117
39276
|
}
|
|
39118
39277
|
};
|
|
@@ -39331,7 +39490,7 @@ function readOwnedRamMib(pid, apiSocket, procRoot = "/proc") {
|
|
|
39331
39490
|
const fields = readVmSmaps(pid, apiSocket, procRoot);
|
|
39332
39491
|
return fields === null ? null : countOwnedMib(fields);
|
|
39333
39492
|
}
|
|
39334
|
-
async function
|
|
39493
|
+
async function readOwnedRamAsync(pid, apiSocket, procRoot = "/proc") {
|
|
39335
39494
|
if (!probeProcessAlive(pid, apiSocket, procRoot)) {
|
|
39336
39495
|
return null;
|
|
39337
39496
|
}
|
|
@@ -39340,19 +39499,27 @@ async function readOwnedRamMibAsync(pid, apiSocket, procRoot = "/proc") {
|
|
|
39340
39499
|
readFile3(`${procRoot}/${String(pid)}/smaps_rollup`, "utf8"),
|
|
39341
39500
|
readFile3(`${procRoot}/${String(pid)}/status`, "utf8")
|
|
39342
39501
|
]);
|
|
39343
|
-
|
|
39502
|
+
const fields = addHugetlbPages(parseProcFields(rollup), status);
|
|
39503
|
+
return {
|
|
39504
|
+
ramMib: countOwnedMib(fields),
|
|
39505
|
+
hugetlbMib: Math.round((fields.get("HugetlbPages") ?? 0) / KIB_PER_MIB)
|
|
39506
|
+
};
|
|
39344
39507
|
} catch {
|
|
39345
39508
|
return null;
|
|
39346
39509
|
}
|
|
39347
39510
|
}
|
|
39348
|
-
async function
|
|
39511
|
+
async function readResidentRamAsync(pid, apiSocket, procRoot = "/proc") {
|
|
39349
39512
|
if (!probeProcessAlive(pid, apiSocket, procRoot)) {
|
|
39350
39513
|
return null;
|
|
39351
39514
|
}
|
|
39352
39515
|
try {
|
|
39353
39516
|
const text = await readFile3(`${procRoot}/${String(pid)}/status`, "utf8");
|
|
39354
39517
|
const fields = parseProcFields(text);
|
|
39355
|
-
|
|
39518
|
+
const hugetlbKb = fields.get("HugetlbPages") ?? 0;
|
|
39519
|
+
return {
|
|
39520
|
+
ramMib: Math.round(((fields.get("RssAnon") ?? 0) + (fields.get("RssShmem") ?? 0) + hugetlbKb) / KIB_PER_MIB),
|
|
39521
|
+
hugetlbMib: Math.round(hugetlbKb / KIB_PER_MIB)
|
|
39522
|
+
};
|
|
39356
39523
|
} catch {
|
|
39357
39524
|
return null;
|
|
39358
39525
|
}
|
|
@@ -41224,7 +41391,7 @@ function createVmReconciler(context, ops) {
|
|
|
41224
41391
|
if (state === "Paused") {
|
|
41225
41392
|
await context.vms.resumeVm(pid, paths);
|
|
41226
41393
|
}
|
|
41227
|
-
await context.admission
|
|
41394
|
+
await context.admission.admit({
|
|
41228
41395
|
id: imp.id,
|
|
41229
41396
|
name: imp.name,
|
|
41230
41397
|
reserveMib: Math.max(meta?.ramMib ?? 0, context.config.wakeReserveMib),
|
|
@@ -41253,7 +41420,7 @@ function createVmReconciler(context, ops) {
|
|
|
41253
41420
|
getImpdLogger("imps").with({ imp: imp.name }).warning("cannot adopt the VM a wake left: {error}; it boots cold next", {
|
|
41254
41421
|
error: getErrorMessage(error)
|
|
41255
41422
|
});
|
|
41256
|
-
context.admission
|
|
41423
|
+
context.admission.release(imp.id);
|
|
41257
41424
|
removeSnapshot(paths);
|
|
41258
41425
|
if (!await stopOrphan(imp.name, pid, paths)) {
|
|
41259
41426
|
return ops.updateState(imp, {
|
|
@@ -42203,7 +42370,7 @@ function makeSleepImpVm(deps) {
|
|
|
42203
42370
|
const failure = isOomKill ? `${OOM_KILL_TRIGGER} (${message})` : message;
|
|
42204
42371
|
getImpdLogger("imps").with({ imp: imp.name }).warning("sleep failed after firecracker stopped: {failure}", { failure });
|
|
42205
42372
|
removeSnapshot(paths);
|
|
42206
|
-
context.admission
|
|
42373
|
+
context.admission.release(imp.id);
|
|
42207
42374
|
await updateState(imp, {
|
|
42208
42375
|
reason: "stopped",
|
|
42209
42376
|
detail: { trigger: isOomKill ? OOM_KILL_TRIGGER : reason },
|
|
@@ -42213,7 +42380,7 @@ function makeSleepImpVm(deps) {
|
|
|
42213
42380
|
});
|
|
42214
42381
|
throw isOomKill ? new Error(`sleep failed: ${OOM_KILL_TRIGGER}`, { cause: error }) : error;
|
|
42215
42382
|
}
|
|
42216
|
-
context.admission
|
|
42383
|
+
context.admission.release(imp.id);
|
|
42217
42384
|
return updateState(imp, {
|
|
42218
42385
|
reason: "slept",
|
|
42219
42386
|
detail: slept.detail,
|
|
@@ -42260,6 +42427,11 @@ function readDiskMib(path) {
|
|
|
42260
42427
|
}
|
|
42261
42428
|
}
|
|
42262
42429
|
|
|
42430
|
+
// packages/daemon/src/imps/imps/pick-huge-pages.ts
|
|
42431
|
+
function pickHugePages(isOn, imp) {
|
|
42432
|
+
return isOn && imp.maxMemoryMib <= imp.memoryMib && imp.memoryMib % 2 === 0 ? "2M" : "None";
|
|
42433
|
+
}
|
|
42434
|
+
|
|
42263
42435
|
// packages/daemon/src/imps/imps/pick-wake-huge-pages.ts
|
|
42264
42436
|
function pickWakeHugePages(recorded, isOn) {
|
|
42265
42437
|
if (recorded !== "2M") {
|
|
@@ -42301,13 +42473,22 @@ function makeWakeImpVm(deps) {
|
|
|
42301
42473
|
throw buildUffdDeviceError();
|
|
42302
42474
|
}
|
|
42303
42475
|
const cgroup = setupVmCgroup(imp);
|
|
42304
|
-
|
|
42305
|
-
|
|
42306
|
-
|
|
42307
|
-
|
|
42308
|
-
|
|
42309
|
-
|
|
42310
|
-
|
|
42476
|
+
try {
|
|
42477
|
+
await context.admission.admit({
|
|
42478
|
+
id: imp.id,
|
|
42479
|
+
name: imp.name,
|
|
42480
|
+
reserveMib: Math.max(meta.ramMib, context.config.wakeReserveMib),
|
|
42481
|
+
memoryMib: imp.maxMemoryMib,
|
|
42482
|
+
hugePagesMib: hugePages === "2M" ? imp.memoryMib : 0
|
|
42483
|
+
});
|
|
42484
|
+
} catch (error) {
|
|
42485
|
+
if (!isOrdinaryBudgetError(error) || !canBootOnPool(imp)) {
|
|
42486
|
+
throw error;
|
|
42487
|
+
}
|
|
42488
|
+
const reason = `${getErrorMessage(error)}, so it boots on the huge page pool`;
|
|
42489
|
+
getImpdLogger("imps").with({ imp: imp.name }).info("cold boot instead of a wake: {reason}", { reason });
|
|
42490
|
+
return startColdImpVm(imp, "wake_fallback", reason);
|
|
42491
|
+
}
|
|
42311
42492
|
context.memoryLimit.setGuestMib(imp.id, imp.memoryMib + (meta.pluggedMib ?? 0));
|
|
42312
42493
|
if (meta.pluggedMib !== undefined) {
|
|
42313
42494
|
context.pluggedSizes.write(imp.id, meta.pluggedMib);
|
|
@@ -42368,6 +42549,7 @@ function makeWakeImpVm(deps) {
|
|
|
42368
42549
|
}
|
|
42369
42550
|
return toLockedImp(imp, { ...imp, isTrustPending: false });
|
|
42370
42551
|
};
|
|
42552
|
+
const canBootOnPool = (imp) => pickHugePages(context.config.hugePages !== null, imp) === "2M";
|
|
42371
42553
|
const writeHostPagesIdentity = (imp, paths) => {
|
|
42372
42554
|
const booted = readVmIdentity(paths);
|
|
42373
42555
|
if (booted !== null) {
|
|
@@ -42400,7 +42582,7 @@ ${failure.message}`, { cause: failure }) : failure;
|
|
|
42400
42582
|
} catch (error) {
|
|
42401
42583
|
const message = getErrorMessage(error);
|
|
42402
42584
|
getImpdLogger("imps").with({ imp: imp.name }).warning("could not stop the woken VM: {message}", { message });
|
|
42403
|
-
context.admission
|
|
42585
|
+
context.admission.release(imp.id);
|
|
42404
42586
|
removeSnapshot(paths);
|
|
42405
42587
|
await updateState(imp, {
|
|
42406
42588
|
reason: "failed",
|
|
@@ -42415,7 +42597,7 @@ ${failure.message}`, { cause: failure }) : failure;
|
|
|
42415
42597
|
const failure = error.message.split(`
|
|
42416
42598
|
`)[0] ?? "";
|
|
42417
42599
|
getImpdLogger("imps").with({ imp: imp.name }).warning("{failure}; booting cold", { failure });
|
|
42418
|
-
context.admission
|
|
42600
|
+
context.admission.release(imp.id);
|
|
42419
42601
|
removeSnapshot(paths);
|
|
42420
42602
|
const stopped = await updateState(imp, {
|
|
42421
42603
|
reason: "stopped",
|
|
@@ -42428,11 +42610,6 @@ ${failure.message}`, { cause: failure }) : failure;
|
|
|
42428
42610
|
return wakeImpVm;
|
|
42429
42611
|
}
|
|
42430
42612
|
|
|
42431
|
-
// packages/daemon/src/imps/imps/pick-huge-pages.ts
|
|
42432
|
-
function pickHugePages(isOn, imp) {
|
|
42433
|
-
return isOn && imp.maxMemoryMib <= imp.memoryMib && imp.memoryMib % 2 === 0 ? "2M" : "None";
|
|
42434
|
-
}
|
|
42435
|
-
|
|
42436
42613
|
// packages/daemon/src/imps/imps/start-cold-vm.ts
|
|
42437
42614
|
import { randomBytes as randomBytes9 } from "crypto";
|
|
42438
42615
|
import { statSync as statSync10 } from "fs";
|
|
@@ -43352,7 +43529,7 @@ function createImpVmOps(context) {
|
|
|
43352
43529
|
if (hugePages === "2M" && !context.probeUffdDevice()) {
|
|
43353
43530
|
throw buildUffdDeviceError();
|
|
43354
43531
|
}
|
|
43355
|
-
await context.admission
|
|
43532
|
+
await context.admission.admit({
|
|
43356
43533
|
id: imp.id,
|
|
43357
43534
|
name: imp.name,
|
|
43358
43535
|
reserveMib: Math.ceil(imp.memoryMib * context.config.bootReservePercent / 100),
|
|
@@ -43411,7 +43588,7 @@ function createImpVmOps(context) {
|
|
|
43411
43588
|
}
|
|
43412
43589
|
return reset.isDiskGrowPending ? await setGrowPending(reset, false) : reset;
|
|
43413
43590
|
} catch (error) {
|
|
43414
|
-
context.admission
|
|
43591
|
+
context.admission.release(imp.id);
|
|
43415
43592
|
await writeFailure(imp, error);
|
|
43416
43593
|
throw error;
|
|
43417
43594
|
}
|
|
@@ -43425,7 +43602,7 @@ function createImpVmOps(context) {
|
|
|
43425
43602
|
}
|
|
43426
43603
|
await context.cgroups.remove(imp.id);
|
|
43427
43604
|
removeSnapshot(paths);
|
|
43428
|
-
context.admission
|
|
43605
|
+
context.admission.release(imp.id);
|
|
43429
43606
|
return imp.state === "stopped" ? imp : updateState(imp, { reason: "stopped", state: "stopped", pid: null });
|
|
43430
43607
|
};
|
|
43431
43608
|
const sleepImpVm = makeSleepImpVm({
|
|
@@ -43489,7 +43666,7 @@ function createImpVmOps(context) {
|
|
|
43489
43666
|
if (imp.pid !== null) {
|
|
43490
43667
|
await context.vms.stopVm(imp.pid, context.findPaths(imp.id), false);
|
|
43491
43668
|
}
|
|
43492
|
-
context.admission
|
|
43669
|
+
context.admission.release(imp.id);
|
|
43493
43670
|
const stopped = await updateState(imp, {
|
|
43494
43671
|
reason: "stopped",
|
|
43495
43672
|
detail: { trigger: reason },
|
|
@@ -44053,6 +44230,7 @@ function createImpService(deps, createWatchdog) {
|
|
|
44053
44230
|
isUnmergeable: (id) => context.mergeFlags.isLost(id),
|
|
44054
44231
|
lockImp: lock.withImp,
|
|
44055
44232
|
lockImpId: (id, action) => lock.withImpId(id, action, { isMove: true }),
|
|
44233
|
+
tryLockImpId: lock.tryWithImpId,
|
|
44056
44234
|
haltImp: ops.stopImpVm,
|
|
44057
44235
|
bootImp: (imp) => ops.startImpVm(imp, "restore"),
|
|
44058
44236
|
requireRunningImp: ops.requireRunningImp,
|
|
@@ -45109,20 +45287,14 @@ function createImpdImps(config, deps, parts) {
|
|
|
45109
45287
|
uffdHandlerBin: config.hugePages?.handlerBin ?? null
|
|
45110
45288
|
});
|
|
45111
45289
|
const {
|
|
45112
|
-
|
|
45113
|
-
|
|
45290
|
+
readVmRam,
|
|
45291
|
+
readResidentRam,
|
|
45114
45292
|
readKsmProfitMib: readProfitMib,
|
|
45115
45293
|
readHugePagePool: readHugePool,
|
|
45116
45294
|
readHugetlbMib,
|
|
45117
45295
|
...host
|
|
45118
45296
|
} = resolveImpBoundaries(deps.imps ?? {});
|
|
45119
|
-
const
|
|
45120
|
-
const admission = {
|
|
45121
|
-
admit: (request) => holder.governor?.admit(request) ?? Promise.resolve(),
|
|
45122
|
-
release: (id) => {
|
|
45123
|
-
holder.governor?.release(id);
|
|
45124
|
-
}
|
|
45125
|
-
};
|
|
45297
|
+
const deferred = buildDeferredAdmission();
|
|
45126
45298
|
const pluggedSizes = createPluggedSizes();
|
|
45127
45299
|
const memoryLimit = deps.imps?.memoryLimit ?? cgroups;
|
|
45128
45300
|
const serviceDeps = {
|
|
@@ -45148,7 +45320,7 @@ function createImpdImps(config, deps, parts) {
|
|
|
45148
45320
|
},
|
|
45149
45321
|
...deps.now !== undefined && { now: deps.now },
|
|
45150
45322
|
...host,
|
|
45151
|
-
admission,
|
|
45323
|
+
admission: deferred.admission,
|
|
45152
45324
|
pluggedSizes
|
|
45153
45325
|
};
|
|
45154
45326
|
const imps = createImpService(serviceDeps, createImpWatchdog);
|
|
@@ -45187,10 +45359,8 @@ function createImpdImps(config, deps, parts) {
|
|
|
45187
45359
|
isLocked: (id) => imps.isImpBusy(id),
|
|
45188
45360
|
isBusy,
|
|
45189
45361
|
tryWhileRunning: imps.tryWhileRunning,
|
|
45190
|
-
admitGrow:
|
|
45191
|
-
releaseGrow:
|
|
45192
|
-
holder.governor?.releaseGrow(id);
|
|
45193
|
-
},
|
|
45362
|
+
admitGrow: deferred.admitGrow,
|
|
45363
|
+
releaseGrow: deferred.releaseGrow,
|
|
45194
45364
|
limit: memoryLimit,
|
|
45195
45365
|
readRamMib: (imp) => host.readRamMib(imp.pid, imp.paths.apiSocket),
|
|
45196
45366
|
setPluggedMib: pluggedSizes.write,
|
|
@@ -45220,8 +45390,8 @@ function createImpdImps(config, deps, parts) {
|
|
|
45220
45390
|
];
|
|
45221
45391
|
});
|
|
45222
45392
|
},
|
|
45223
|
-
|
|
45224
|
-
|
|
45393
|
+
readVmRam,
|
|
45394
|
+
readResidentRam,
|
|
45225
45395
|
readHugePool,
|
|
45226
45396
|
readHugetlbMib,
|
|
45227
45397
|
readHeadroomMib,
|
|
@@ -45233,7 +45403,7 @@ function createImpdImps(config, deps, parts) {
|
|
|
45233
45403
|
events: imps.events,
|
|
45234
45404
|
...deps.now !== undefined && { now: deps.now }
|
|
45235
45405
|
});
|
|
45236
|
-
|
|
45406
|
+
deferred.attach(governor);
|
|
45237
45407
|
startImpTelemetry({
|
|
45238
45408
|
bus: imps.events,
|
|
45239
45409
|
subscribeResources: imps.subscribeResources,
|
|
@@ -45258,8 +45428,8 @@ function resolveImpBoundaries(boundaries) {
|
|
|
45258
45428
|
ramMib: readRamMib(pid, apiSocket),
|
|
45259
45429
|
rssMib: (readRss ?? readRssMib)(pid, apiSocket)
|
|
45260
45430
|
}),
|
|
45261
|
-
|
|
45262
|
-
|
|
45431
|
+
readVmRam: readRam === undefined ? readOwnedRamAsync : makeStandInVmRam(readRam),
|
|
45432
|
+
readResidentRam: readRam === undefined ? readResidentRamAsync : makeStandInVmRam(readRam),
|
|
45263
45433
|
readUnsharedRamMib: boundaries.readUnsharedRamMib ?? readUnsharedRamMib,
|
|
45264
45434
|
checkGuestMerge: boundaries.checkGuestMerge ?? checkGuestMemoryMergeable,
|
|
45265
45435
|
probeUffdDevice: boundaries.probeUffdDevice ?? (() => probeUffdDevice()),
|
|
@@ -45277,6 +45447,13 @@ function resolveImpBoundaries(boundaries) {
|
|
|
45277
45447
|
...boundaries.now !== undefined && { now: boundaries.now }
|
|
45278
45448
|
};
|
|
45279
45449
|
}
|
|
45450
|
+
function makeStandInVmRam(readRam) {
|
|
45451
|
+
return (pid, apiSocket) => {
|
|
45452
|
+
const ramMib = readRam(pid, apiSocket);
|
|
45453
|
+
const reading = ramMib === null ? null : { ramMib, hugetlbMib: 0 };
|
|
45454
|
+
return Promise.resolve(reading);
|
|
45455
|
+
};
|
|
45456
|
+
}
|
|
45280
45457
|
|
|
45281
45458
|
// packages/daemon/src/imp-ops/moves/move-receiver.ts
|
|
45282
45459
|
import { existsSync as existsSync16, rmSync as rmSync24 } from "fs";
|
|
@@ -45355,6 +45532,7 @@ var FilteredGrantSchema2 = z79.object({ secret: z79.string(), filter: GrantFilte
|
|
|
45355
45532
|
var ImpSchema2 = z79.object({
|
|
45356
45533
|
id: z79.uuid(),
|
|
45357
45534
|
name: z79.string(),
|
|
45535
|
+
clonedDigest: z79.string().optional(),
|
|
45358
45536
|
vcpus: z79.int().positive(),
|
|
45359
45537
|
memoryMib: z79.int().positive(),
|
|
45360
45538
|
maxMemoryMib: z79.int().positive().optional(),
|
|
@@ -45605,7 +45783,7 @@ function readReceipt(receipt, secret) {
|
|
|
45605
45783
|
import { mkdirSync as mkdirSync19, rmSync as rmSync23 } from "fs";
|
|
45606
45784
|
import { open as open7 } from "fs/promises";
|
|
45607
45785
|
import { join as join24 } from "path";
|
|
45608
|
-
import
|
|
45786
|
+
import invariant5 from "tiny-invariant";
|
|
45609
45787
|
|
|
45610
45788
|
// packages/daemon/src/imp-ops/moves/build-received-digest.ts
|
|
45611
45789
|
import { createHash as createHash5 } from "crypto";
|
|
@@ -46266,6 +46444,7 @@ async function readMoveStream(deps, options) {
|
|
|
46266
46444
|
id: header.imp.id,
|
|
46267
46445
|
name: header.imp.name,
|
|
46268
46446
|
image: image.name,
|
|
46447
|
+
...header.imp.clonedDigest !== undefined && { imageDigest: header.imp.clonedDigest },
|
|
46269
46448
|
vcpus: header.imp.vcpus,
|
|
46270
46449
|
memoryMib: header.imp.memoryMib,
|
|
46271
46450
|
maxMemoryMib: header.imp.maxMemoryMib,
|
|
@@ -46395,7 +46574,7 @@ async function requireImage(deps, options) {
|
|
|
46395
46574
|
{ kind: "image-rootfs", sha256: rootfs.sha256, bytes: rootfs.bytes },
|
|
46396
46575
|
{ kind: "image-config", sha256: config.sha256, bytes: config.bytes }
|
|
46397
46576
|
];
|
|
46398
|
-
|
|
46577
|
+
invariant5(rootfs.contentSha256 !== null && config.contentSha256 !== null);
|
|
46399
46578
|
const digest = buildReceivedDigest(rootfs.contentSha256, config.contentSha256);
|
|
46400
46579
|
const known = await readImageInUse(deps.db, [digest], deps.now());
|
|
46401
46580
|
if (known !== undefined) {
|
|
@@ -46704,7 +46883,7 @@ function readSettled2(row, imp) {
|
|
|
46704
46883
|
}
|
|
46705
46884
|
|
|
46706
46885
|
// packages/daemon/src/imp-ops/moves/move-sender.ts
|
|
46707
|
-
import
|
|
46886
|
+
import invariant6 from "tiny-invariant";
|
|
46708
46887
|
import { match as match10 } from "ts-pattern";
|
|
46709
46888
|
|
|
46710
46889
|
// packages/daemon/src/platform/db/move-send-steps.ts
|
|
@@ -47000,6 +47179,7 @@ async function readHeader(deps, options) {
|
|
|
47000
47179
|
imp: {
|
|
47001
47180
|
id: imp.id,
|
|
47002
47181
|
name: imp.name,
|
|
47182
|
+
clonedDigest: imp.imageDigest,
|
|
47003
47183
|
vcpus: imp.vcpus,
|
|
47004
47184
|
memoryMib: imp.memoryMib,
|
|
47005
47185
|
maxMemoryMib: imp.maxMemoryMib,
|
|
@@ -47275,7 +47455,7 @@ function createMoveSender(deps) {
|
|
|
47275
47455
|
log.info("a send cut short by a restart was undone");
|
|
47276
47456
|
}
|
|
47277
47457
|
}).with("commit", async () => {
|
|
47278
|
-
|
|
47458
|
+
invariant6(row !== undefined && row.peer_url !== null && row.ticket !== null);
|
|
47279
47459
|
const moved = imp.moveState === "moved" ? imp : await updateImpMove(deps.db, imp.id, "moved");
|
|
47280
47460
|
await runCommitAfterRestart(moved, row.peer_url, row.ticket);
|
|
47281
47461
|
}).with("none", () => {}).exhaustive();
|
|
@@ -52935,7 +53115,7 @@ function wireTailnetNames(options) {
|
|
|
52935
53115
|
api: createServicesApi({ readCredential: () => readOAuthCredential(names.oauthFile) }),
|
|
52936
53116
|
serve: createServiceServe((argv) => runChecked(argv, {}, options.runCommand ?? runCommand)),
|
|
52937
53117
|
findPort: (slot) => deriveSlotAddress(slot, options.config).tailnetPort,
|
|
52938
|
-
isImpPresent:
|
|
53118
|
+
isImpPresent: makePresenceCheck(options.imps, options.db),
|
|
52939
53119
|
readSuffix: async () => {
|
|
52940
53120
|
const status = await options.readTailscale();
|
|
52941
53121
|
const suffix = status.dnsName?.split(".").slice(1).join(".") ?? "";
|
|
@@ -52943,20 +53123,17 @@ function wireTailnetNames(options) {
|
|
|
52943
53123
|
}
|
|
52944
53124
|
});
|
|
52945
53125
|
}
|
|
52946
|
-
function
|
|
53126
|
+
function makePresenceCheck(imps, db) {
|
|
52947
53127
|
return async (name) => {
|
|
52948
|
-
|
|
52949
|
-
|
|
52950
|
-
|
|
52951
|
-
|
|
52952
|
-
|
|
52953
|
-
|
|
52954
|
-
if (isImpdError(error, "MOVING")) {
|
|
52955
|
-
const imp = await readImpByName(db, name);
|
|
52956
|
-
return imp !== undefined && imp.moveState !== "moved";
|
|
52957
|
-
}
|
|
52958
|
-
throw error;
|
|
53128
|
+
const imp = await readImpByName(db, name);
|
|
53129
|
+
if (imp === undefined) {
|
|
53130
|
+
return false;
|
|
53131
|
+
}
|
|
53132
|
+
if (imp.moveState !== null) {
|
|
53133
|
+
return imp.moveState !== "moved";
|
|
52959
53134
|
}
|
|
53135
|
+
const result = await imps.tryLockImpId(imp.id, (locked) => Promise.resolve(locked !== undefined));
|
|
53136
|
+
return !result.ran || result.value;
|
|
52960
53137
|
};
|
|
52961
53138
|
}
|
|
52962
53139
|
|
|
@@ -58833,7 +59010,7 @@ async function startTestImpd(stack, options = {}) {
|
|
|
58833
59010
|
clock.offsetMs += durationMs;
|
|
58834
59011
|
},
|
|
58835
59012
|
setClock: (nowMs) => {
|
|
58836
|
-
|
|
59013
|
+
invariant7(options.frozenClockMs !== undefined, "setClock needs a frozen clock");
|
|
58837
59014
|
clock.frozenAt = nowMs;
|
|
58838
59015
|
clock.offsetMs = 0;
|
|
58839
59016
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@impz/testing",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.1",
|
|
4
4
|
"description": "A real impd on stand-ins, for Bun tests of code that drives impd through @impz/client",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"ts-pattern": "^5.9.2",
|
|
44
44
|
"xstate": "^5.33.3",
|
|
45
45
|
"zod": "^4.6.7",
|
|
46
|
-
"@impz/client": "6.1.
|
|
46
|
+
"@impz/client": "6.1.1"
|
|
47
47
|
},
|
|
48
48
|
"engines": {
|
|
49
49
|
"bun": ">=1.4.3"
|