Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/loader/container-loader/src/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1021,12 +1021,17 @@ export class Container
(this.isInteractiveClient &&
this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad")) ??
options.enableOfflineLoad === true;
let storageOnly = false;
if (this.readOnlyInfo.readonly === true) {
storageOnly = this.readOnlyInfo.storageOnly;
}
this.serializedStateManager = new SerializedStateManager(
pendingLocalState,
this.subLogger,
this.storageAdapter,
offlineLoadEnabled,
this,
storageOnly,
() => this._deltaManager.connectionManager.shouldJoinWrite(),
() => this.supportGetSnapshotApi(),
this.mc.config.getNumber("Fluid.Container.snapshotRefreshTimeoutMs"),
Expand Down
20 changes: 11 additions & 9 deletions packages/loader/container-loader/src/serializedStateManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export class SerializedStateManager {
private latestSnapshot: ISnapshotInfo | undefined;
private _refreshSnapshotP: Promise<number> | undefined;
private readonly lastSavedOpSequenceNumber: number = 0;
private readonly refreshTimer: Timer;
private readonly refreshTimer: Timer | undefined;
private readonly snapshotRefreshTimeoutMs: number = 60 * 60 * 24 * 1000;

/**
Expand All @@ -166,6 +166,7 @@ export class SerializedStateManager {
private readonly storageAdapter: ISerializedStateManagerDocumentStorageService,
private readonly _offlineLoadEnabled: boolean,
containerEvent: IEventProvider<ISerializerEvent>,
private readonly storageOnly: boolean,
private readonly containerDirty: () => boolean,
private readonly supportGetSnapshotApi: () => boolean,
snapshotRefreshTimeoutMs?: number,
Expand All @@ -176,9 +177,9 @@ export class SerializedStateManager {
});

this.snapshotRefreshTimeoutMs = snapshotRefreshTimeoutMs ?? this.snapshotRefreshTimeoutMs;
this.refreshTimer = new Timer(this.snapshotRefreshTimeoutMs, () =>
this.tryRefreshSnapshot(),
);
this.refreshTimer = this.storageOnly
? undefined
: new Timer(this.snapshotRefreshTimeoutMs, () => this.tryRefreshSnapshot());
// special case handle. Obtaining the last saved op seq num to avoid
// refreshing the snapshot before we have processed it. It could cause
// a subsequent stashing to have a newer snapshot than allowed.
Expand Down Expand Up @@ -244,7 +245,7 @@ export class SerializedStateManager {
snapshotBlobs,
snapshotSequenceNumber: attributes.sequenceNumber,
};
this.refreshTimer.start();
this.refreshTimer?.start();
}
return { baseSnapshot, version };
} else {
Expand Down Expand Up @@ -276,7 +277,8 @@ export class SerializedStateManager {
if (
this.mc.config.getBoolean("Fluid.Container.enableOfflineSnapshotRefresh") === true &&
this._refreshSnapshotP === undefined &&
this.latestSnapshot === undefined
this.latestSnapshot === undefined &&
!this.storageOnly
) {
// Don't block on the refresh snapshot call - it is for the next time we serialize, not booting this incarnation
this._refreshSnapshotP = this.refreshLatestSnapshot(this.supportGetSnapshotApi());
Expand Down Expand Up @@ -361,14 +363,14 @@ export class SerializedStateManager {
stashedSnapshotSequenceNumber: this.snapshot?.snapshotSequenceNumber,
});
this.latestSnapshot = undefined;
this.refreshTimer.restart();
this.refreshTimer?.restart();
} else if (snapshotSequenceNumber <= lastProcessedOpSequenceNumber) {
// Snapshot seq num is between the first and last processed op.
// Remove the ops that are already part of the snapshot
this.processedOps.splice(0, snapshotSequenceNumber - firstProcessedOpSequenceNumber + 1);
this.snapshot = this.latestSnapshot;
this.latestSnapshot = undefined;
this.refreshTimer.restart();
this.refreshTimer?.restart();
this.mc.logger.sendTelemetryEvent({
eventName: "SnapshotRefreshed",
snapshotSequenceNumber,
Expand Down Expand Up @@ -410,7 +412,7 @@ export class SerializedStateManager {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
snapshotSequenceNumber: attributes.sequenceNumber as number,
};
this.refreshTimer.start();
this.refreshTimer?.start();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ describe("serializedStateManager", () => {
storageAdapter,
false,
eventEmitter,
false,
() => false,
() => false,
);
Expand All @@ -217,6 +218,7 @@ describe("serializedStateManager", () => {
}),
true,
eventEmitter,
false,
() => false,
() => false,
);
Expand Down Expand Up @@ -246,6 +248,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
() => false,
() => false,
);
Expand All @@ -269,6 +272,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
() => false,
() => false,
);
Expand Down Expand Up @@ -304,6 +308,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
() => false,
() => false,
);
Expand Down Expand Up @@ -355,6 +360,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
() => false,
() => false,
);
Expand Down Expand Up @@ -418,6 +424,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
() => false,
() => false,
);
Expand Down Expand Up @@ -459,6 +466,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
isDirtyF,
() => false,
);
Expand Down Expand Up @@ -519,6 +527,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
isDirtyF,
() => false,
);
Expand Down Expand Up @@ -571,6 +580,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
isDirtyF,
() => false,
);
Expand Down Expand Up @@ -634,6 +644,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
() => isDirty,
() => false,
);
Expand Down Expand Up @@ -666,6 +677,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
isDirtyF,
() => false,
);
Expand Down Expand Up @@ -737,6 +749,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
isDirtyF,
() => false,
);
Expand Down Expand Up @@ -787,6 +800,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
() => isDirty,
() => false,
);
Expand Down Expand Up @@ -822,6 +836,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
() => isDirty,
() => false,
);
Expand Down Expand Up @@ -873,6 +888,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
isDirtyF,
() => false,
);
Expand Down Expand Up @@ -974,6 +990,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
isDirtyF,
() => false,
snapshotRefreshTimeoutMs,
Expand Down Expand Up @@ -1043,6 +1060,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
isDirtyF,
() => false,
snapshotRefreshTimeoutMs,
Expand Down Expand Up @@ -1112,6 +1130,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
isDirtyF,
() => false,
snapshotRefreshTimeoutMs,
Expand Down Expand Up @@ -1176,6 +1195,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
isDirtyF,
() => false,
snapshotRefreshTimeoutMs,
Expand Down Expand Up @@ -1246,6 +1266,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
isDirtyF,
() => false,
snapshotRefreshTimeoutMs,
Expand Down Expand Up @@ -1313,6 +1334,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
isDirtyF,
() => false,
snapshotRefreshTimeoutMs,
Expand Down Expand Up @@ -1363,6 +1385,39 @@ describe("serializedStateManager", () => {
lastProcessedOpSequenceNumber,
);
});

it(`no snapshot refresh on storage only mode. isDirty: ${isDirty}`, async () => {
const storageAdapter = new MockStorageAdapter();
const saved = false;
const isDirtyF = (): boolean => (saved ? false : isDirty);
const serializedStateManager = new SerializedStateManager(
undefined,
enableOfflineSnapshotRefresh(logger),
storageAdapter,
true,
eventEmitter,
true,
isDirtyF,
() => false,
snapshotRefreshTimeoutMs,
);

await serializedStateManager.fetchSnapshot(undefined);
const lastProcessedOpSequenceNumber = 20;
let seq = 1;
while (seq <= lastProcessedOpSequenceNumber) {
serializedStateManager.addProcessedOp(generateSavedOp(seq++));
}
const snapshotSequenceNumber = 11; // latest snapshot will be among processed ops
storageAdapter.uploadSummary(snapshotSequenceNumber);
// snapshot refresh promise is undefined before timeout
const snapshotRefreshP = serializedStateManager.refreshSnapshotP;
assert.strictEqual(snapshotRefreshP, undefined);
clock.tick(snapshotRefreshTimeoutMs);
// now it's a promise
const initialRefreshP = serializedStateManager.refreshSnapshotP;
assert.strictEqual(initialRefreshP, undefined, "no refresh expected");
});
}
});
});
Loading