@fluidframework/runtime-utils 0.52.0-44610 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dataStoreHelpers.d.ts +0 -1
- package/dist/dataStoreHelpers.d.ts.map +1 -1
- package/dist/dataStoreHelpers.js +18 -31
- package/dist/dataStoreHelpers.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/remoteObjectHandle.d.ts +0 -4
- package/dist/remoteObjectHandle.d.ts.map +1 -1
- package/dist/remoteObjectHandle.js +0 -6
- package/dist/remoteObjectHandle.js.map +1 -1
- package/dist/summarizerNode/summarizerNodeWithGc.d.ts +1 -13
- package/dist/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summarizerNode/summarizerNodeWithGc.js +3 -73
- package/dist/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/dataStoreHelpers.d.ts +0 -1
- package/lib/dataStoreHelpers.d.ts.map +1 -1
- package/lib/dataStoreHelpers.js +17 -29
- package/lib/dataStoreHelpers.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/remoteObjectHandle.d.ts +0 -4
- package/lib/remoteObjectHandle.d.ts.map +1 -1
- package/lib/remoteObjectHandle.js +0 -6
- package/lib/remoteObjectHandle.js.map +1 -1
- package/lib/summarizerNode/summarizerNodeWithGc.d.ts +1 -13
- package/lib/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summarizerNode/summarizerNodeWithGc.js +4 -74
- package/lib/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/package.json +12 -11
- package/src/dataStoreHelpers.ts +24 -29
- package/src/packageVersion.ts +1 -1
- package/src/remoteObjectHandle.ts +0 -7
- package/src/summarizerNode/summarizerNodeWithGc.ts +3 -92
package/src/dataStoreHelpers.ts
CHANGED
|
@@ -16,24 +16,13 @@ import {
|
|
|
16
16
|
IFluidDataStoreRegistry,
|
|
17
17
|
IProvideFluidDataStoreRegistry,
|
|
18
18
|
} from "@fluidframework/runtime-definitions";
|
|
19
|
+
import { generateErrorWithStack } from "@fluidframework/telemetry-utils";
|
|
19
20
|
|
|
20
21
|
interface IResponseException extends Error {
|
|
21
22
|
errorFromRequestFluidObject: true;
|
|
22
23
|
message: string;
|
|
23
24
|
code: number;
|
|
24
|
-
stack
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function getStack() {
|
|
28
|
-
const err = new Error();
|
|
29
|
-
if (err.stack !== undefined) {
|
|
30
|
-
return err.stack;
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
throw err;
|
|
34
|
-
} catch (err2) {
|
|
35
|
-
return (err2 as Error).stack;
|
|
36
|
-
}
|
|
25
|
+
stack?: string;
|
|
37
26
|
}
|
|
38
27
|
|
|
39
28
|
export function exceptionToResponse(err: any): IResponse {
|
|
@@ -45,31 +34,33 @@ export function exceptionToResponse(err: any): IResponse {
|
|
|
45
34
|
mimeType: "text/plain",
|
|
46
35
|
status: responseErr.code,
|
|
47
36
|
value: responseErr.message,
|
|
48
|
-
stack
|
|
37
|
+
get stack() { return responseErr.stack; },
|
|
49
38
|
};
|
|
50
39
|
}
|
|
40
|
+
|
|
41
|
+
// Capture error objects, not stack itself, as stack retrieval is very expensive operation, so we delay it
|
|
42
|
+
const errWithStack = generateErrorWithStack();
|
|
43
|
+
|
|
51
44
|
return {
|
|
52
45
|
mimeType: "text/plain",
|
|
53
46
|
status,
|
|
54
47
|
value: `${err}`,
|
|
55
|
-
stack
|
|
48
|
+
get stack() { return ((err?.stack) as (string|undefined)) ?? errWithStack.stack; },
|
|
56
49
|
};
|
|
57
50
|
}
|
|
58
51
|
|
|
59
|
-
export function responseToException(response: IResponse, request: IRequest) {
|
|
52
|
+
export function responseToException(response: IResponse, request: IRequest): Error {
|
|
60
53
|
const message = response.value;
|
|
61
|
-
const
|
|
62
|
-
const responseErr
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
return err;
|
|
54
|
+
const errWithStack = generateErrorWithStack();
|
|
55
|
+
const responseErr: Error & IResponseException = {
|
|
56
|
+
errorFromRequestFluidObject: true,
|
|
57
|
+
message,
|
|
58
|
+
name: "Error",
|
|
59
|
+
code: response.status,
|
|
60
|
+
get stack() { return response.stack ?? errWithStack.stack; },
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return responseErr;
|
|
73
64
|
}
|
|
74
65
|
|
|
75
66
|
export async function requestFluidObject<T = IFluidObject & FluidObject>(
|
|
@@ -91,11 +82,15 @@ export function createResponseError(status: number, value: string, request: IReq
|
|
|
91
82
|
assert(status !== 200, 0x19b /* "Cannot not create response error on 200 status" */);
|
|
92
83
|
// Omit query string which could contain personal data (aka "PII")
|
|
93
84
|
const urlNoQuery = request.url?.split("?")[0];
|
|
85
|
+
|
|
86
|
+
// Capture error objects, not stack itself, as stack retrieval is very expensive operation, so we delay it
|
|
87
|
+
const errWithStack = generateErrorWithStack();
|
|
88
|
+
|
|
94
89
|
return {
|
|
95
90
|
mimeType: "text/plain",
|
|
96
91
|
status,
|
|
97
92
|
value: urlNoQuery === undefined ? value : `${value}: ${urlNoQuery}`,
|
|
98
|
-
stack
|
|
93
|
+
get stack() { return errWithStack.stack; },
|
|
99
94
|
};
|
|
100
95
|
}
|
|
101
96
|
|
package/src/packageVersion.ts
CHANGED
|
@@ -41,13 +41,6 @@ export class RemoteFluidObjectHandle implements IFluidHandle {
|
|
|
41
41
|
assert(absolutePath.startsWith("/"), 0x19d /* "Handles should always have absolute paths" */);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
/**
|
|
45
|
-
* @deprecated - This returns the absolute path.
|
|
46
|
-
*/
|
|
47
|
-
public get path() {
|
|
48
|
-
return this.absolutePath;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
44
|
public async get(): Promise<any> {
|
|
52
45
|
if (this.objectP === undefined) {
|
|
53
46
|
const request = { url: this.absolutePath };
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
|
-
import { assert, LazyPromise
|
|
7
|
+
import { assert, LazyPromise } from "@fluidframework/common-utils";
|
|
8
8
|
import { cloneGCData } from "@fluidframework/garbage-collector";
|
|
9
|
-
import {
|
|
9
|
+
import { ISnapshotTree } from "@fluidframework/protocol-definitions";
|
|
10
10
|
import {
|
|
11
11
|
CreateChildSummarizerNodeParam,
|
|
12
12
|
gcBlobKey,
|
|
@@ -27,8 +27,6 @@ import {
|
|
|
27
27
|
SummaryNode,
|
|
28
28
|
} from "./summarizerNodeUtils";
|
|
29
29
|
|
|
30
|
-
const defaultMaxUnreferencedDurationMs = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
31
|
-
|
|
32
30
|
export interface IRootSummarizerNodeWithGC extends ISummarizerNodeWithGC, ISummarizerNodeRootContract {}
|
|
33
31
|
|
|
34
32
|
// Extend SummaryNode to add used routes tracking to it.
|
|
@@ -75,22 +73,9 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
75
73
|
// If this node is marked as unreferenced, the time when it marked as such.
|
|
76
74
|
private unreferencedTimestampMs: number | undefined;
|
|
77
75
|
|
|
78
|
-
// The max duration for which this node can be unreferenced before it is eligible for deletion.
|
|
79
|
-
private readonly maxUnreferencedDurationMs: number;
|
|
80
|
-
|
|
81
|
-
// The timer that runs when the node is marked unreferenced.
|
|
82
|
-
private readonly unreferencedTimer: Timer;
|
|
83
|
-
|
|
84
|
-
// Tracks whether this node is inactive after being unreferenced for maxUnreferencedDurationMs.
|
|
85
|
-
private inactive: boolean = false;
|
|
86
|
-
|
|
87
76
|
// True if GC is disabled for this node. If so, do not track GC specific state for a summary.
|
|
88
77
|
private readonly gcDisabled: boolean;
|
|
89
78
|
|
|
90
|
-
// Keeps track of whether we logged an use-after-free error. This is temporary since the error is too noisy.
|
|
91
|
-
// To be removed once this item is completed - https://github.com/microsoft/FluidFramework/issues/7895.
|
|
92
|
-
private useAfterFreeErrorLogged: boolean = false;
|
|
93
|
-
|
|
94
79
|
/**
|
|
95
80
|
* Do not call constructor directly.
|
|
96
81
|
* Use createRootSummarizerNodeWithGC to create root node, or createChild to create child nodes.
|
|
@@ -118,16 +103,11 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
118
103
|
);
|
|
119
104
|
|
|
120
105
|
this.gcDisabled = config.gcDisabled === true;
|
|
121
|
-
this.maxUnreferencedDurationMs = config.maxUnreferencedDurationMs ?? defaultMaxUnreferencedDurationMs;
|
|
122
106
|
|
|
123
107
|
this.gcDetailsInInitialSummaryP = new LazyPromise(async () => {
|
|
124
108
|
const gcSummaryDetails = await getInitialGCSummaryDetailsFn?.();
|
|
125
109
|
return gcSummaryDetails ?? { usedRoutes: [] };
|
|
126
110
|
});
|
|
127
|
-
|
|
128
|
-
this.unreferencedTimer = new Timer(this.maxUnreferencedDurationMs, () => {
|
|
129
|
-
this.inactive = true;
|
|
130
|
-
});
|
|
131
111
|
}
|
|
132
112
|
|
|
133
113
|
// Returns the GC details that may be added to this node's summary.
|
|
@@ -334,7 +314,6 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
334
314
|
...config,
|
|
335
315
|
// Propagate our gcDisabled state to the child if its not explicity specified in child's config.
|
|
336
316
|
gcDisabled: config.gcDisabled ?? this.gcDisabled,
|
|
337
|
-
maxUnreferencedDurationMs: config.maxUnreferencedDurationMs ?? this.maxUnreferencedDurationMs,
|
|
338
317
|
},
|
|
339
318
|
createDetails.changeSequenceNumber,
|
|
340
319
|
createDetails.latestSummary,
|
|
@@ -382,82 +361,14 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
|
|
|
382
361
|
}
|
|
383
362
|
|
|
384
363
|
if (this.isReferenced()) {
|
|
385
|
-
// If this node has been unreferenced for longer than maxUnreferencedDurationMs and is being referenced,
|
|
386
|
-
// log an error as this may mean the maxUnreferencedDurationMs is not long enough.
|
|
387
|
-
this.logErrorIfInactive("inactiveObjectRevived", gcTimestamp);
|
|
388
|
-
|
|
389
|
-
// Clear unreferenced / inactive state, if any.
|
|
390
|
-
this.inactive = false;
|
|
391
364
|
this.unreferencedTimestampMs = undefined;
|
|
392
|
-
this.unreferencedTimer.clear();
|
|
393
365
|
return;
|
|
394
366
|
}
|
|
395
367
|
|
|
396
|
-
//
|
|
397
|
-
// timer which would mark the node as inactive.
|
|
398
|
-
|
|
399
|
-
// If there is no timestamp when GC was run, we don't have enough information to determine whether this content
|
|
400
|
-
// should become inactive.
|
|
401
|
-
if (gcTimestamp === undefined) {
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// If unreferencedTimestampMs is not present, this node just became unreferenced. Update unreferencedTimestampMs
|
|
406
|
-
// and start the unreferenced timer.
|
|
407
|
-
// Note that it's possible this node has been unreferenced before but the unreferencedTimestampMs was not added.
|
|
408
|
-
// For example, older versions where this concept did not exist or if gcTimestamp wasn't available. In such
|
|
409
|
-
// cases, we track them as if the content just became unreferenced.
|
|
368
|
+
// If this node just became unreferenced, update its unreferencedTimestampMs.
|
|
410
369
|
if (this.unreferencedTimestampMs === undefined) {
|
|
411
370
|
this.unreferencedTimestampMs = gcTimestamp;
|
|
412
|
-
this.unreferencedTimer.start();
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// If we are here, this node was unreferenced earlier.
|
|
417
|
-
|
|
418
|
-
// If it is already inactive or has an unreferenced timer running, there is no more work to be done.
|
|
419
|
-
if (this.inactive || this.unreferencedTimer.hasTimer) {
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// If it has been unreferenced longer than maxUnreferencedDurationMs, mark it as inactive. Otherwise, start the
|
|
424
|
-
// unreferenced timer for the duration left for it to reach maxUnreferencedDurationMs.
|
|
425
|
-
const currentUnreferencedDurationMs = gcTimestamp - this.unreferencedTimestampMs;
|
|
426
|
-
if (currentUnreferencedDurationMs >= this.maxUnreferencedDurationMs) {
|
|
427
|
-
this.inactive = true;
|
|
428
|
-
} else {
|
|
429
|
-
this.unreferencedTimer.start(this.maxUnreferencedDurationMs - currentUnreferencedDurationMs);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
public recordChange(op: ISequencedDocumentMessage): void {
|
|
434
|
-
// If the node is changed after it is inactive, log an error as this may mean use-after-delete.
|
|
435
|
-
// Currently, we only log this error once per node per session as it's too noisy.
|
|
436
|
-
if (!this.useAfterFreeErrorLogged && this.logErrorIfInactive("inactiveObjectChanged")) {
|
|
437
|
-
this.useAfterFreeErrorLogged = true;
|
|
438
|
-
}
|
|
439
|
-
super.recordChange(op);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* Logs an error event if the node is inactive. This is used to identify cases where an inactive object is used.
|
|
444
|
-
* @param eventName - The name of the event to log.
|
|
445
|
-
* @param currentTimestampMs - The current time stamp. Used to report how long the object has been inactive.
|
|
446
|
-
* @returns true if we logged an error, false otherwise.
|
|
447
|
-
*/
|
|
448
|
-
private logErrorIfInactive(eventName: string, currentTimestampMs?: number): boolean {
|
|
449
|
-
if (this.inactive) {
|
|
450
|
-
assert(
|
|
451
|
-
this.unreferencedTimestampMs !== undefined,
|
|
452
|
-
0x271 /* "Node should not become inactive without setting unreferencedTimestampMs first" */);
|
|
453
|
-
this.defaultLogger.sendErrorEvent({
|
|
454
|
-
eventName,
|
|
455
|
-
unreferencedDuratonMs: (currentTimestampMs ?? Date.now()) - this.unreferencedTimestampMs,
|
|
456
|
-
maxUnreferencedDurationMs: this.maxUnreferencedDurationMs,
|
|
457
|
-
});
|
|
458
|
-
return true;
|
|
459
371
|
}
|
|
460
|
-
return false;
|
|
461
372
|
}
|
|
462
373
|
|
|
463
374
|
/**
|