Abstract base classes and helpers for implementing container runtime factories with standardized lifecycle management. These utilities provide a foundation for creating runtime factories that handle container initialization, loading, and lifecycle events.
⚠️ Legacy API Warning: The
RuntimeFactoryHelperclass is part of the legacy API surface and is only available via the/legacyimport path. This API may be deprecated in future versions. Consider using modern runtime factory patterns from the main package exports.
Abstract base class that provides the standard implementation pattern for runtime factories, with hooks for customizing initialization behavior.
Import Path: @fluidframework/runtime-utils/legacy
⚠️ This is a legacy API that may change or be removed in future versions. Use with caution in production code.
/**
* Helper base class for implementing runtime factories
* @template T - The type of runtime to create, defaults to IContainerRuntime
*/
abstract class RuntimeFactoryHelper<T = IContainerRuntime> implements IRuntimeFactory {
/** Get the IRuntimeFactory interface */
get IRuntimeFactory(): this;
/**
* Main runtime instantiation method called by the container
* @param context - The container context
* @param existing - Whether this is loading an existing container (true) or creating new (false)
* @returns Promise resolving to the created runtime
*/
instantiateRuntime(context: IContainerContext, existing: boolean): Promise<IRuntime>;
/**
* Called at the start of initializing a container to create the container runtime instance
* Must be implemented by subclasses to create the specific runtime type
* @param context - The container context
* @param existing - Whether this is loading an existing container
* @returns Promise resolving to the runtime instance
*/
abstract preInitialize(
context: IContainerContext,
existing: boolean
): Promise<IRuntime & T>;
/**
* Called the one time the container is created (not on subsequent loads)
* Override to perform first-time initialization logic
* @param runtime - The runtime instance that was created
* @returns Promise that resolves when initialization is complete
*/
instantiateFirstTime(runtime: T): Promise<void>;
/**
* Called every time the container runtime is loaded for an existing container
* Override to perform load-time initialization logic
* @param runtime - The runtime instance that was loaded
* @returns Promise that resolves when initialization is complete
*/
instantiateFromExisting(runtime: T): Promise<void>;
/**
* Called at the end of initializing a container
* Override to perform final initialization steps
* @param runtime - The runtime instance that has been initialized
* @returns Promise that resolves when finalization is complete
*/
hasInitialized(runtime: T): Promise<void>;
}Usage Examples:
import { RuntimeFactoryHelper } from "@fluidframework/runtime-utils/legacy";
import { ContainerRuntime, IContainerRuntimeOptions } from "@fluidframework/container-runtime";
// Custom runtime factory implementation
class MyRuntimeFactory extends RuntimeFactoryHelper<ContainerRuntime> {
constructor(
private readonly registryEntries: NamedFluidDataStoreRegistryEntries,
private readonly runtimeOptions?: IContainerRuntimeOptions
) {
super();
}
// Required: Create the runtime instance
async preInitialize(
context: IContainerContext,
existing: boolean
): Promise<IRuntime & ContainerRuntime> {
const runtime = await ContainerRuntime.loadRuntime({
context,
registryEntries: this.registryEntries,
existing,
runtimeOptions: this.runtimeOptions,
});
return runtime;
}
// Optional: First-time container creation logic
async instantiateFirstTime(runtime: ContainerRuntime): Promise<void> {
console.log("Creating new container - setting up initial data stores");
// Create default data stores
await runtime.createDataStore("main-data-store");
await runtime.createDataStore("settings-data-store");
// Set up initial configuration
const configDataStore = await runtime.createDataStore("config");
const config = await configDataStore.entryPoint.get();
await config.initialize({
version: "1.0",
created: new Date().toISOString()
});
}
// Optional: Existing container load logic
async instantiateFromExisting(runtime: ContainerRuntime): Promise<void> {
console.log("Loading existing container - performing migration checks");
// Check for data migrations
const configDataStore = await runtime.getDataStore("config");
const config = await configDataStore.entryPoint.get();
const currentVersion = await config.getVersion();
if (needsMigration(currentVersion)) {
await performMigration(runtime, currentVersion);
}
}
// Optional: Final initialization logic
async hasInitialized(runtime: ContainerRuntime): Promise<void> {
console.log("Runtime initialization complete - setting up monitoring");
// Set up telemetry and monitoring
runtime.on("dispose", () => {
console.log("Runtime disposed");
});
runtime.on("connected", () => {
console.log("Runtime connected");
});
runtime.on("disconnected", () => {
console.log("Runtime disconnected");
});
}
}
// Usage
const runtimeFactory = new MyRuntimeFactory(
[
["my-data-store", Promise.resolve(MyDataStoreFactory)],
["settings-store", Promise.resolve(SettingsStoreFactory)]
],
{
summaryOptions: { summaryConfigOverrides: { state: "disabled" } },
gcOptions: { gcAllowed: true }
}
);
// The runtime factory can now be used with container loading
const container = await loader.createDetachedContainer(codeDetails);// Multi-tenant runtime factory
class MultiTenantRuntimeFactory extends RuntimeFactoryHelper<ContainerRuntime> {
constructor(
private tenantId: string,
private tenantConfig: TenantConfiguration
) {
super();
}
async preInitialize(
context: IContainerContext,
existing: boolean
): Promise<IRuntime & ContainerRuntime> {
// Customize runtime options based on tenant
const runtimeOptions: IContainerRuntimeOptions = {
summaryOptions: this.tenantConfig.summaryOptions,
gcOptions: this.tenantConfig.gcOptions,
compressionOptions: this.tenantConfig.compressionOptions
};
return ContainerRuntime.loadRuntime({
context,
registryEntries: this.getRegistryForTenant(this.tenantId),
existing,
runtimeOptions
});
}
async instantiateFirstTime(runtime: ContainerRuntime): Promise<void> {
// Set up tenant-specific initial state
await this.createTenantDataStores(runtime);
await this.applyTenantPolicies(runtime);
}
async instantiateFromExisting(runtime: ContainerRuntime): Promise<void> {
// Validate tenant access and apply updates
await this.validateTenantAccess(runtime);
await this.updateTenantPolicies(runtime);
}
private async createTenantDataStores(runtime: ContainerRuntime): Promise<void> {
const tenantStores = this.tenantConfig.requiredDataStores;
for (const storeConfig of tenantStores) {
await runtime.createDataStore(storeConfig.type, storeConfig.id);
}
}
}
// Development/Production runtime factory
class EnvironmentAwareRuntimeFactory extends RuntimeFactoryHelper<ContainerRuntime> {
constructor(
private environment: "development" | "production",
private baseRegistryEntries: NamedFluidDataStoreRegistryEntries
) {
super();
}
async preInitialize(
context: IContainerContext,
existing: boolean
): Promise<IRuntime & ContainerRuntime> {
const runtimeOptions = this.getRuntimeOptionsForEnvironment();
const registryEntries = this.getRegistryEntriesForEnvironment();
return ContainerRuntime.loadRuntime({
context,
registryEntries,
existing,
runtimeOptions
});
}
async hasInitialized(runtime: ContainerRuntime): Promise<void> {
if (this.environment === "development") {
// Add development-only debugging and logging
this.setupDevelopmentTooling(runtime);
} else {
// Add production monitoring and telemetry
this.setupProductionMonitoring(runtime);
}
}
private getRuntimeOptionsForEnvironment(): IContainerRuntimeOptions {
if (this.environment === "development") {
return {
summaryOptions: { summaryConfigOverrides: { state: "disabled" } },
gcOptions: { gcAllowed: false }, // Disable GC in dev for easier debugging
compressionOptions: { minimumBatchSizeInBytes: 0 } // No compression in dev
};
} else {
return {
summaryOptions: { summaryConfigOverrides: { state: "enabled" } },
gcOptions: { gcAllowed: true },
compressionOptions: { minimumBatchSizeInBytes: 1024 }
};
}
}
}
// Testing-focused runtime factory
class TestRuntimeFactory extends RuntimeFactoryHelper<ContainerRuntime> {
private testHooks: TestHooks[] = [];
addTestHook(hook: TestHook): void {
this.testHooks.push(hook);
}
async preInitialize(
context: IContainerContext,
existing: boolean
): Promise<IRuntime & ContainerRuntime> {
// Create runtime with test-friendly options
const runtime = await ContainerRuntime.loadRuntime({
context,
registryEntries: this.getTestRegistryEntries(),
existing,
runtimeOptions: {
summaryOptions: { summaryConfigOverrides: { state: "disabled" } },
gcOptions: { gcAllowed: false }
}
});
// Apply test hooks
for (const hook of this.testHooks) {
hook.apply(runtime);
}
return runtime;
}
async instantiateFirstTime(runtime: ContainerRuntime): Promise<void> {
// Create predictable test data
await this.createTestDataStores(runtime);
}
private async createTestDataStores(runtime: ContainerRuntime): Promise<void> {
// Create data stores with known IDs for testing
await runtime.createDataStore("test-store-1", "test-1");
await runtime.createDataStore("test-store-2", "test-2");
}
}