Skip to content

Commit

Permalink
Avoid using temporary directory if possible (#720)
Browse files Browse the repository at this point in the history
Miniflare 2 persisted data in-memory between `setOptions()` calls when
`*Persist` options were disabled. Whilst `workerd` has in-memory
storage for Durable Objects, this is reset when `workerd` is
restarted (i.e. when `setOptions()` is called). To retain Miniflare 2
behaviour in Miniflare 3, we actually persist to a temporary directory
when `*Persist` options are disabled. There are cases where we don't
need the temporary directory though: if we have no storage bindings
configured, `cache` is `false`, and `unsafeEphemeralDurableObjects` is
`true`. This change ensures we don't write to the temporary directory
in these cases.
  • Loading branch information
mrbbot committed Nov 1, 2023
1 parent 1438598 commit 8cbe08d
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 48 deletions.
3 changes: 3 additions & 0 deletions packages/miniflare/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,8 @@ export class Miniflare {
}

// Collect all services required by this worker
const unsafeEphemeralDurableObjects =
workerOpts.core.unsafeEphemeralDurableObjects ?? false;
const pluginServicesOptionsBase: Omit<
PluginServicesOptions<z.ZodTypeAny, undefined>,
"options" | "sharedOptions"
Expand All @@ -954,6 +956,7 @@ export class Miniflare {
tmpPath: this.#tmpPath,
workerNames,
durableObjectClassNames,
unsafeEphemeralDurableObjects,
queueConsumers,
};
for (const [key, plugin] of PLUGIN_ENTRIES) {
Expand Down
95 changes: 48 additions & 47 deletions packages/miniflare/src/plugins/cache/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,57 +85,58 @@ export const CACHE_PLUGIN: Plugin<
],
};
}
const services: Service[] = [
{ name: getCacheServiceName(workerIndex), worker: entryWorker },
];

const uniqueKey = `miniflare-${CACHE_OBJECT_CLASS_NAME}`;
if (cache) {
const uniqueKey = `miniflare-${CACHE_OBJECT_CLASS_NAME}`;

const persist = sharedOptions.cachePersist;
const persistPath = getPersistPath(CACHE_PLUGIN_NAME, tmpPath, persist);
await fs.mkdir(persistPath, { recursive: true });
const storageService: Service = {
name: CACHE_STORAGE_SERVICE_NAME,
disk: { path: persistPath, writable: true },
};
const objectService: Service = {
name: CACHE_SERVICE_PREFIX,
worker: {
compatibilityDate: "2023-07-24",
compatibilityFlags: ["nodejs_compat", "experimental"],
modules: [
{
name: "cache.worker.js",
esModule: SCRIPT_CACHE_OBJECT(),
},
],
durableObjectNamespaces: [
{
className: CACHE_OBJECT_CLASS_NAME,
uniqueKey,
},
],
// Store Durable Object SQL databases in persist path
durableObjectStorage: { localDisk: CACHE_STORAGE_SERVICE_NAME },
// Bind blob disk directory service to object
bindings: [
{
name: SharedBindings.MAYBE_SERVICE_BLOBS,
service: { name: CACHE_STORAGE_SERVICE_NAME },
},
{
name: SharedBindings.MAYBE_SERVICE_LOOPBACK,
service: { name: SERVICE_LOOPBACK },
},
],
},
};
const persist = sharedOptions.cachePersist;
const persistPath = getPersistPath(CACHE_PLUGIN_NAME, tmpPath, persist);
await fs.mkdir(persistPath, { recursive: true });
const storageService: Service = {
name: CACHE_STORAGE_SERVICE_NAME,
disk: { path: persistPath, writable: true },
};
const objectService: Service = {
name: CACHE_SERVICE_PREFIX,
worker: {
compatibilityDate: "2023-07-24",
compatibilityFlags: ["nodejs_compat", "experimental"],
modules: [
{
name: "cache.worker.js",
esModule: SCRIPT_CACHE_OBJECT(),
},
],
durableObjectNamespaces: [
{
className: CACHE_OBJECT_CLASS_NAME,
uniqueKey,
},
],
// Store Durable Object SQL databases in persist path
durableObjectStorage: { localDisk: CACHE_STORAGE_SERVICE_NAME },
// Bind blob disk directory service to object
bindings: [
{
name: SharedBindings.MAYBE_SERVICE_BLOBS,
service: { name: CACHE_STORAGE_SERVICE_NAME },
},
{
name: SharedBindings.MAYBE_SERVICE_LOOPBACK,
service: { name: SERVICE_LOOPBACK },
},
],
},
};
services.push(storageService, objectService);

// NOTE: not migrating here as applications should be able to recover from
// cache evictions, and we'd need to locate all named caches
// NOTE: not migrating here as applications should be able to recover from
// cache evictions, and we'd need to locate all named caches
}

const services: Service[] = [
{ name: getCacheServiceName(workerIndex), worker: entryWorker },
storageService,
objectService,
];
return services;
},
};
12 changes: 11 additions & 1 deletion packages/miniflare/src/plugins/do/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,12 @@ export const DURABLE_OBJECTS_PLUGIN: Plugin<
const objects = Object.keys(options.durableObjects ?? {});
return Object.fromEntries(objects.map((name) => [name, kProxyNodeBinding]));
},
async getServices({ sharedOptions, tmpPath, durableObjectClassNames }) {
async getServices({
sharedOptions,
tmpPath,
durableObjectClassNames,
unsafeEphemeralDurableObjects,
}) {
// Check if we even have any Durable Object bindings, if we don't, we can
// skip creating the storage directory
let hasDurableObjects = false;
Expand All @@ -83,6 +88,11 @@ export const DURABLE_OBJECTS_PLUGIN: Plugin<
}
if (!hasDurableObjects) return;

// If this worker has enabled `unsafeEphemeralDurableObjects`, it won't need
// the Durable Object storage service. If all workers have this enabled, we
// don't need to create the storage service at all.
if (unsafeEphemeralDurableObjects) return;

const storagePath = getPersistPath(
DURABLE_OBJECTS_PLUGIN_NAME,
tmpPath,
Expand Down
1 change: 1 addition & 0 deletions packages/miniflare/src/plugins/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface PluginServicesOptions<

// ~~Leaky abstractions~~ "Plugin specific options" :)
durableObjectClassNames: DurableObjectClassNames;
unsafeEphemeralDurableObjects: boolean;
queueConsumers: QueueConsumers;
}

Expand Down

0 comments on commit 8cbe08d

Please sign in to comment.