@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.
Files changed (2) hide show
  1. package/dist/index.js +339 -162
  2. 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 invariant6 from "tiny-invariant";
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.0",
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
- constructor(message, shortfall) {
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
- function buildImpOverBudgetError(budgetMib, usedMib, memoryMib) {
24983
- return new RamBudgetError(`the imp's memory, at its max (${String(memoryMib)} MiB), is larger than the whole RAM budget (${String(budgetMib)} MiB)`, { budgetMib, usedMib, requestedMib: memoryMib, protected: [] });
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, memory_mib AS memoryMib,
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
- let query = db.updateTable("imps").set({ ...values, ...buildAwakeValues(change), ...buildBootCauseValues(change) }).where("id", "=", id);
27082
- if (expected !== undefined) {
27083
- if (change.state !== expected.state) {
27084
- requireTransition(expected.state, change.state, `move to ${change.state}`);
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
- const pidOperator = expected.pid === null ? "is" : "=";
27087
- query = query.where("state", "=", expected.state).where("pid", pidOperator, expected.pid);
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
- const returning = query.returningAll();
27090
- const row = expected === undefined ? await returning.executeTakeFirstOrThrow() : await returning.executeTakeFirst();
27091
- return row === undefined ? undefined : emitChange(db, toImpRecord(row), change);
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: source.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 sql12 } from "kysely";
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 sql12`
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 sql12`SELECT name FROM kysely_migration`.execute(db);
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.lockImp(imp.name, (locked) => locked.id === imp.id ? createCopy(locked, runId) : Promise.reject(new Error("replaced since the database copy")));
29259
- copies.set(imp.id, made);
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 = async (request, hugePagesMib) => {
37620
- const listed = await deps.listAwake();
37621
- const awake = listed.filter((imp) => imp.id !== request.id);
37622
- for (const imp of awake) {
37623
- pendingHugePages.delete(imp.id);
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
- usedMib: poolAdmission.committedMib,
37646
- reserveMib: hugePagesMib,
37647
- neededMib: poolAdmission.neededMib,
37648
- protectedCount: 0
37769
+ protectedCount: 0,
37770
+ ...refusal
37649
37771
  });
37650
37772
  }
37651
- throw buildHugePoolError({
37652
- totalMib: poolAdmission.totalMib,
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
- const readings = await Promise.all(awake.map((imp) => readVmMib(imp.pid, imp.apiSocket)));
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 measured = readings[index] ?? 0;
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
- return { awake, byImp, usedMib, effectiveMib, headroomMib };
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, findMissing) => {
37817
+ const readFreeRoom = async (excludeId, findShortfall) => {
37683
37818
  const usage = await readEffectiveUsage(excludeId);
37684
- const missingMib = findMissing(usage);
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 missingMib = options.findMissing(usage);
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 = usage.awake.map((imp) => ({
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
- if (request.memoryMib > deps.budgetMib) {
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
- emitAdmission(request, "refused", usage.effectiveMib, {
37774
- neededMib: buildNeededMib(usage.effectiveMib, request.memoryMib, deps.budgetMib),
37775
- protectedCount: 0
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.readResidentMib);
37784
- if (bound.effectiveMib + request.reserveMib <= deps.budgetMib) {
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 findMissing = (usage) => usage.effectiveMib + request.reserveMib - deps.budgetMib;
37789
- const room = request.maySleepImps === false ? await readFreeRoom(request.id, findMissing) : await makeRoom({
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
- findMissing
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
- emitAdmission(request, "refused", usage.effectiveMib, {
37802
- neededMib: buildNeededMib(usage.effectiveMib, request.reserveMib, deps.budgetMib),
37803
- protectedCount: room.protected.length
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
- throw buildRamBudgetError({
37806
- budgetMib: deps.budgetMib,
37807
- usedMib: usage.effectiveMib,
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
- findMissing: (usage) => usage.effectiveMib + request.mib - deps.budgetMib,
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: usage.effectiveMib,
37990
+ usedMib: shortfall.usedMib,
37837
37991
  reserveMib: request.mib,
37838
37992
  ...!fits && {
37839
- neededMib: buildNeededMib(usage.effectiveMib, request.mib, deps.budgetMib),
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
- findMissing: (usage) => usage.usedMib - deps.budgetMib
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?.release(imp.id);
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?.admit({
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?.release(id);
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 readOwnedRamMibAsync(pid, apiSocket, procRoot = "/proc") {
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
- return countOwnedMib(addHugetlbPages(parseProcFields(rollup), status));
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 readResidentMibAsync(pid, apiSocket, procRoot = "/proc") {
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
- return Math.round(((fields.get("RssAnon") ?? 0) + (fields.get("RssShmem") ?? 0) + (fields.get("HugetlbPages") ?? 0)) / KIB_PER_MIB);
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?.admit({
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?.release(imp.id);
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?.release(imp.id);
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?.release(imp.id);
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
- await context.admission?.admit({
42305
- id: imp.id,
42306
- name: imp.name,
42307
- reserveMib: Math.max(meta.ramMib, context.config.wakeReserveMib),
42308
- memoryMib: imp.maxMemoryMib,
42309
- hugePagesMib: hugePages === "2M" ? imp.memoryMib : 0
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?.release(imp.id);
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?.release(imp.id);
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?.admit({
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?.release(imp.id);
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?.release(imp.id);
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?.release(imp.id);
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
- readRamMibAsync,
45113
- readResidentMib,
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 holder = { governor: null };
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: (request) => holder.governor?.admitGrow(request) ?? Promise.resolve(true),
45191
- releaseGrow: (id) => {
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
- readRamMib: readRamMibAsync,
45224
- readResidentMib,
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
- holder.governor = governor;
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
- readRamMibAsync: readRam === undefined ? readOwnedRamMibAsync : (pid, apiSocket) => Promise.resolve(readRam(pid, apiSocket)),
45262
- readResidentMib: readRam === undefined ? readResidentMibAsync : (pid, apiSocket) => Promise.resolve(readRam(pid, apiSocket)),
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 invariant4 from "tiny-invariant";
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
- invariant4(rootfs.contentSha256 !== null && config.contentSha256 !== null);
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 invariant5 from "tiny-invariant";
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
- invariant5(row !== undefined && row.peer_url !== null && row.ticket !== null);
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: createPresenceCheck(options.imps, options.db),
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 createPresenceCheck(imps, db) {
53126
+ function makePresenceCheck(imps, db) {
52947
53127
  return async (name) => {
52948
- try {
52949
- return await imps.lockImp(name, () => Promise.resolve(true));
52950
- } catch (error) {
52951
- if (isImpdError(error, "NOT_FOUND")) {
52952
- return false;
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
- invariant6(options.frozenClockMs !== undefined, "setClock needs a frozen clock");
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.0",
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.0"
46
+ "@impz/client": "6.1.1"
47
47
  },
48
48
  "engines": {
49
49
  "bun": ">=1.4.3"