Skip to content

Commit

Permalink
Throw when loading fails in SharedString (#4615)
Browse files Browse the repository at this point in the history
fixes #4612
releated #4613
  • Loading branch information
anthony-murphy authored Dec 16, 2020
1 parent c06be22 commit dfcb17d
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 2 deletions.
3 changes: 3 additions & 0 deletions packages/dds/merge-tree/src/snapshotLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ export class SnapshotLoader {
const catchupOpsP =
this.loadBodyAndCatchupOps(headerLoadedP, services);

catchupOpsP.catch(
(err)=>this.logger.sendErrorEvent({ eventName: "CatchupOpsLoadFailure" },err));

await headerLoadedP;

return { catchupOpsP };
Expand Down
6 changes: 5 additions & 1 deletion packages/dds/sequence/src/sequence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ export abstract class SharedSegmentSequence<T extends MergeTree.ISegment>
) {
super(id, dataStoreRuntime, attributes);

this.loadedDeferred.promise.catch((error)=>{
this.logger.sendErrorEvent({ eventName: "SequenceLoadFailed" }, error);
});

this.client = new MergeTree.Client(
segmentFromSpec,
ChildLogger.create(this.logger, "SharedSegmentSequence.MergeTreeClient"),
Expand Down Expand Up @@ -654,8 +658,8 @@ export abstract class SharedSegmentSequence<T extends MergeTree.ISegment>
// Initialize the interval collections
this.initializeIntervalCollections();
if (error) {
this.logger.sendErrorEvent({ eventName: "SequenceLoadFailed" }, error);
this.loadedDeferred.reject(error);
throw error;
} else {
// it is important this series remains synchronous
// first we stop defering incoming ops, and apply then all
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ const tests = (args: ITestObjectProvider) => {
const newContainer = await args.loadTestContainer(testContainerConfig) as Container;
const newComponent = await requestFluidObject<ITestFluidObject>(newContainer, "default");
const newSharedString = await newComponent.getSharedObject<SharedString>(stringId);
assert.equal(newSharedString.getText(), text, "The new container should receive the inserted text on creation");
assert.equal(
newSharedString.getText(), text, "The new container should receive the inserted text on creation");
});
};

Expand Down
132 changes: 132 additions & 0 deletions packages/test/end-to-end-tests/src/test/sharedStringLoading.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

import { strict as assert } from "assert";
import { Loader } from "@fluidframework/container-loader";
import { requestFluidObject } from "@fluidframework/runtime-utils";
import { SharedString } from "@fluidframework/sequence";
import {
ChannelFactoryRegistry,
ITestFluidObject,
LocalCodeLoader,
SupportedExportInterfaces,
TestFluidObjectFactory,
} from "@fluidframework/test-utils";
import { LocalDeltaConnectionServer } from "@fluidframework/server-local-server";
import { LocalDocumentServiceFactory, LocalResolver } from "@fluidframework/local-driver";
import {
IDocumentService,
IDocumentServiceFactory,
IDocumentStorageService,
LoaderCachingPolicy,
} from "@fluidframework/driver-definitions";
import { NetworkErrorBasic } from "@fluidframework/driver-utils";
import { IFluidHandle } from "@fluidframework/core-interfaces";

describe("SharedString", () => {
it("Failure to Load in Shared String", async ()=>{
const deltaConnectionServer = LocalDeltaConnectionServer.create();
const stringId = "sharedStringKey";
const registry: ChannelFactoryRegistry = [[stringId, SharedString.getFactory()]];
const fluidExport: SupportedExportInterfaces = {
IFluidDataStoreFactory: new TestFluidObjectFactory(registry),
};
const text = "hello world";
const documentId = "sstest";
{ // creating client
const urlResolver = new LocalResolver();
const documentServiceFactory = new LocalDocumentServiceFactory(deltaConnectionServer);
const codeDetails = { package: "no-dynamic-pkg" };
const codeLoader = new LocalCodeLoader([
[codeDetails, fluidExport],
]);

const loader = new Loader({
urlResolver,
documentServiceFactory,
codeLoader,
});

const container = await loader.createDetachedContainer(codeDetails);
const dataObject = await requestFluidObject<ITestFluidObject>(container, "default");
const sharedString = await dataObject.root.get<IFluidHandle<SharedString>>(stringId).get();
sharedString.insertText(0, text);

await container.attach(urlResolver.createCreateNewRequest(documentId));
}
{ // normal load client
const urlResolver = new LocalResolver();
const documentServiceFactory = new LocalDocumentServiceFactory(deltaConnectionServer);
const codeDetails = { package: "no-dynamic-pkg" };
const codeLoader = new LocalCodeLoader([
[codeDetails, fluidExport],
]);

const loader = new Loader({
urlResolver,
documentServiceFactory,
codeLoader,
});

const container = await loader.resolve(urlResolver.createCreateNewRequest(documentId));
const dataObject = await requestFluidObject<ITestFluidObject>(container, "default");
const sharedString = await dataObject.root.get<IFluidHandle<SharedString>>(stringId).get();
assert.strictEqual(sharedString.getText(0), text);
}
{ // failure load client
const urlResolver = new LocalResolver();
const realSf: IDocumentServiceFactory =
new LocalDocumentServiceFactory(deltaConnectionServer);
const documentServiceFactory: IDocumentServiceFactory = {
...realSf,
createDocumentService: async (resolvedUrl,logger) => {
const realDs = await realSf.createDocumentService(resolvedUrl, logger);
const mockDs = Object.create(realDs) as IDocumentService;
mockDs.policies = {
... mockDs.policies,
caching: LoaderCachingPolicy.NoCaching,
};
mockDs.connectToStorage = async ()=>{
const realStorage = await realDs.connectToStorage();
const mockstorage = Object.create(realStorage) as IDocumentStorageService;
mockstorage.read = async (id)=>{
const blob = await realStorage.read(id);
const blobObj = JSON.parse(Buffer.from(blob, "Base64").toString());
// throw when trying to load the header blob
if (blobObj.headerMetadata !== undefined) {
throw new NetworkErrorBasic(
"Not Found",
undefined,
false,
404);
}
return blob;
};
return mockstorage;
};
return mockDs;
},
};
const codeDetails = { package: "no-dynamic-pkg" };
const codeLoader = new LocalCodeLoader([
[codeDetails, fluidExport],
]);

const loader = new Loader({
urlResolver,
documentServiceFactory,
codeLoader,
});

const container = await loader.resolve(urlResolver.createCreateNewRequest(documentId));
const dataObject = await requestFluidObject<ITestFluidObject>(container, "default");
// TODO: this should probably throw, but currently returns the dataObject due to #4613
// this test should be updated when that issue is fix
const sharedString = await dataObject.root.get<IFluidHandle<SharedString>>(stringId).get();
// eslint-disable-next-line dot-notation
assert.strictEqual(sharedString["getText"], undefined);
}
});
});

0 comments on commit dfcb17d

Please sign in to comment.