Skip to content

Commit

Permalink
Use lazy class for delay initialization of services in localchannelCo…
Browse files Browse the repository at this point in the history
…ntext (microsoft#3599)
  • Loading branch information
jatgarg committed Sep 11, 2020
1 parent 9d9fe50 commit 7da2719
Showing 1 changed file with 29 additions and 32 deletions.
61 changes: 29 additions & 32 deletions packages/runtime/datastore/src/localChannelContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { IFluidDataStoreContext, ISummarizeResult } from "@fluidframework/runtim
import { readAndParse } from "@fluidframework/driver-utils";
import { CreateContainerError } from "@fluidframework/container-utils";
import { convertToSummaryTree } from "@fluidframework/runtime-utils";
import { Lazy } from "@fluidframework/common-utils";
import { createServiceEndpoints, IChannelContext, snapshotChannel } from "./channelContext";
import { ChannelDeltaConnection } from "./channelDeltaConnection";
import { ISharedObjectRegistry } from "./dataStoreRuntime";
Expand All @@ -33,10 +34,10 @@ export class LocalChannelContext implements IChannelContext {
private _isLoaded = false;
private attached = false;
private readonly pending: ISequencedDocumentMessage[] = [];
private _services: {
private readonly services: Lazy<{
readonly deltaConnection: ChannelDeltaConnection,
readonly objectStorage: ChannelStorageService,
} | undefined;
}>;
private readonly dirtyFn: () => void;
private readonly factory: IChannelFactory | undefined;

Expand All @@ -51,6 +52,23 @@ export class LocalChannelContext implements IChannelContext {
dirtyFn: (address: string) => void,
private readonly snapshotTree: ISnapshotTree | undefined,
) {
this.services = new Lazy(() => {
let blobMap: Map<string, string> | undefined;
if (this.snapshotTree !== undefined) {
blobMap = new Map<string, string>();
this.collectExtraBlobsAndSanitizeSnapshot(this.snapshotTree, blobMap);
}
return createServiceEndpoints(
this.id,
this.dataStoreContext.connected,
this.submitFn,
this.dirtyFn,
this.storageService,
this.snapshotTree !== undefined ? Promise.resolve(this.snapshotTree) : undefined,
blobMap !== undefined ?
Promise.resolve(blobMap) : undefined,
);
});
this.factory = registry.get(type);
if (this.factory === undefined) {
throw new Error(`Channel Factory ${type} not registered`);
Expand Down Expand Up @@ -78,7 +96,7 @@ export class LocalChannelContext implements IChannelContext {
if (!this.attached) {
return;
}
this.services.deltaConnection.setConnectionState(connected);
this.services.value.deltaConnection.setConnectionState(connected);
}

public processOp(message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown): void {
Expand All @@ -88,7 +106,7 @@ export class LocalChannelContext implements IChannelContext {
// delay loading. So after the container is attached and some other client joins which start generating
// ops for this channel. So not loaded local channel can still receive ops and we store them to process later.
if (this.isLoaded) {
this.services.deltaConnection.process(message, local, localOpMetadata);
this.services.value.deltaConnection.process(message, local, localOpMetadata);
} else {
assert.strictEqual(local, false,
"Should always be remote because a local dds shouldn't generate ops before loading");
Expand All @@ -99,7 +117,7 @@ export class LocalChannelContext implements IChannelContext {
public reSubmit(content: any, localOpMetadata: unknown) {
assert(this.isLoaded, "Channel should be loaded to resubmit ops");
assert(this.attached, "Local channel must be attached when resubmitting op");
this.services.deltaConnection.reSubmit(content, localOpMetadata);
this.services.value.deltaConnection.reSubmit(content, localOpMetadata);
}

public async snapshot(fullTree: boolean = false): Promise<ITree> {
Expand All @@ -121,16 +139,16 @@ export class LocalChannelContext implements IChannelContext {
assert(!this.isLoaded, "Channel must not already be loaded when loading");
assert(this.snapshotTree, "Snapshot should be provided to load from!!");

assert(await this.services.objectStorage.contains(".attributes"), ".attributes blob should be present");
assert(await this.services.value.objectStorage.contains(".attributes"), ".attributes blob should be present");
const attributes = await readAndParse<IChannelAttributes>(
this.services.objectStorage,
this.services.value.objectStorage,
".attributes");

assert(this.factory, "Factory should be there for local channel");
const channel = await this.factory.load(
this.runtime,
this.id,
this.services,
this.services.value,
undefined,
attributes);

Expand All @@ -139,13 +157,13 @@ export class LocalChannelContext implements IChannelContext {
this._isLoaded = true;

if (this.attached) {
this.channel.connect(this.services);
this.channel.connect(this.services.value);
}

// Send all pending messages to the channel
for (const message of this.pending) {
try {
this.services.deltaConnection.process(message, false, undefined /* localOpMetadata */);
this.services.value.deltaConnection.process(message, false, undefined /* localOpMetadata */);
} catch (err) {
// record sequence number for easier debugging
const error = CreateContainerError(err);
Expand All @@ -163,32 +181,11 @@ export class LocalChannelContext implements IChannelContext {

if (this.isLoaded) {
assert(this.channel, "Channel should be there if loaded!!");
this.channel.connect(this.services);
this.channel.connect(this.services.value);
}
this.attached = true;
}

private get services() {
if (this._services === undefined) {
let blobMap: Map<string, string> | undefined;
if (this.snapshotTree !== undefined) {
blobMap = new Map<string, string>();
this.collectExtraBlobsAndSanitizeSnapshot(this.snapshotTree, blobMap);
}
this._services = createServiceEndpoints(
this.id,
this.dataStoreContext.connected,
this.submitFn,
this.dirtyFn,
this.storageService,
this.snapshotTree !== undefined ? Promise.resolve(this.snapshotTree) : undefined,
blobMap !== undefined ?
Promise.resolve(blobMap) : undefined,
);
}
return this._services;
}

private collectExtraBlobsAndSanitizeSnapshot(snapshotTree: ISnapshotTree, blobMap: Map<string, string>) {
const blobMapInitial = new Map(Object.entries(snapshotTree.blobs));
for (const [blobName, blobId] of blobMapInitial.entries()) {
Expand Down

0 comments on commit 7da2719

Please sign in to comment.