Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you probably want to make this a getter, as the state can change

() => 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 @@ -1166,6 +1185,39 @@ describe("serializedStateManager", () => {
);
});

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");
});

it(`load flow. saved event before fetching the snapshot isDirty: ${isDirty}`, async () => {
const storageAdapter = new MockStorageAdapter();
let saved = false;
Expand All @@ -1176,6 +1228,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
isDirtyF,
() => false,
snapshotRefreshTimeoutMs,
Expand Down Expand Up @@ -1246,6 +1299,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
isDirtyF,
() => false,
snapshotRefreshTimeoutMs,
Expand Down Expand Up @@ -1313,6 +1367,7 @@ describe("serializedStateManager", () => {
storageAdapter,
true,
eventEmitter,
false,
isDirtyF,
() => false,
snapshotRefreshTimeoutMs,
Expand Down
1 change: 1 addition & 0 deletions packages/test/local-server-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"devDependencies": {
"@biomejs/biome": "~1.9.3",
"@fluid-experimental/tree": "workspace:~",
"@fluid-internal/client-utils": "workspace:~",
"@fluid-internal/mocha-test-setup": "workspace:~",
"@fluid-private/test-drivers": "workspace:~",
"@fluid-private/test-pairwise-generator": "workspace:~",
Expand Down
Loading
Loading