@fluidframework/driver-web-cache 2.0.0-internal.3.0.2 → 2.0.0-internal.3.2.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/.eslintrc.js +11 -11
- package/README.md +15 -16
- package/api-extractor.json +2 -2
- package/dist/FluidCache.d.ts.map +1 -1
- package/dist/FluidCache.js +2 -2
- package/dist/FluidCache.js.map +1 -1
- package/dist/FluidCacheIndexedDb.d.ts.map +1 -1
- package/dist/FluidCacheIndexedDb.js.map +1 -1
- package/dist/fluidCacheTelemetry.d.ts.map +1 -1
- package/dist/fluidCacheTelemetry.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/scheduleIdleTask.js.map +1 -1
- package/jest.config.js +5 -5
- package/lib/FluidCache.d.ts.map +1 -1
- package/lib/FluidCache.js +2 -2
- package/lib/FluidCache.js.map +1 -1
- package/lib/FluidCacheIndexedDb.d.ts.map +1 -1
- package/lib/FluidCacheIndexedDb.js.map +1 -1
- package/lib/fluidCacheTelemetry.d.ts.map +1 -1
- package/lib/fluidCacheTelemetry.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/scheduleIdleTask.js.map +1 -1
- package/package.json +35 -34
- package/prettier.config.cjs +1 -1
- package/src/FluidCache.ts +224 -251
- package/src/FluidCacheIndexedDb.ts +100 -116
- package/src/fluidCacheTelemetry.ts +8 -8
- package/src/packageVersion.ts +1 -1
- package/src/scheduleIdleTask.ts +50 -53
- package/tsconfig.esnext.json +6 -6
- package/tsconfig.json +10 -14
|
@@ -18,138 +18,122 @@ export const FluidDriverObjectStoreName = "driverStorage.V3";
|
|
|
18
18
|
export const CurrentCacheVersion = 3;
|
|
19
19
|
|
|
20
20
|
// Note that V1 and V2 were misspelled as "diver", and we need to keep using the misspelling here.
|
|
21
|
-
export const oldVersionNameMapping: Partial<{ [key: number]: string
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
export const oldVersionNameMapping: Partial<{ [key: number]: string }> = {
|
|
22
|
+
1: "diverStorage",
|
|
23
|
+
2: "diverStorage.V2",
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
export function getKeyForCacheEntry(entry: ICacheEntry) {
|
|
27
|
-
|
|
27
|
+
return `${entry.file.docId}_${entry.type}_${entry.key}`;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export function getFluidCacheIndexedDbInstance(
|
|
31
|
-
|
|
31
|
+
logger?: ITelemetryBaseLogger,
|
|
32
32
|
): Promise<IDBPDatabase<FluidCacheDBSchema>> {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
cacheObjectStore.createIndex(
|
|
73
|
-
"partitionKey",
|
|
74
|
-
"partitionKey",
|
|
75
|
-
);
|
|
76
|
-
cacheObjectStore.createIndex("fileId", "fileId");
|
|
77
|
-
},
|
|
78
|
-
blocked: () => {
|
|
79
|
-
reject(
|
|
80
|
-
new Error(
|
|
81
|
-
"Could not open DB since it is blocked by an older client that has the DB open",
|
|
82
|
-
),
|
|
83
|
-
);
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
).then(resolve, reject);
|
|
87
|
-
});
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
openDB<FluidCacheDBSchema>(FluidDriverCacheDBName, CurrentCacheVersion, {
|
|
35
|
+
upgrade: (db, oldVersion) => {
|
|
36
|
+
try {
|
|
37
|
+
// We changed the format of the object store, so we must
|
|
38
|
+
// delete the old stores to create a new one in V3
|
|
39
|
+
const cacheToDelete = oldVersionNameMapping[oldVersion];
|
|
40
|
+
if (cacheToDelete) {
|
|
41
|
+
// We don't include the old object stores in the schema, so we need to
|
|
42
|
+
// use a typecast here to prevent IDB from complaining
|
|
43
|
+
db.deleteObjectStore(cacheToDelete as any);
|
|
44
|
+
}
|
|
45
|
+
} catch (error: any) {
|
|
46
|
+
// Catch any error done when attempting to delete the older version.
|
|
47
|
+
// If the object does not exist db will throw.
|
|
48
|
+
// We can now assume that the old version is no longer there regardless.
|
|
49
|
+
ChildLogger.create(logger).sendErrorEvent(
|
|
50
|
+
{
|
|
51
|
+
eventName: FluidCacheErrorEvent.FluidCacheDeleteOldDbError,
|
|
52
|
+
},
|
|
53
|
+
error,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const cacheObjectStore = db.createObjectStore(FluidDriverObjectStoreName);
|
|
58
|
+
cacheObjectStore.createIndex("createdTimeMs", "createdTimeMs");
|
|
59
|
+
cacheObjectStore.createIndex("lastAccessTimeMs", "lastAccessTimeMs");
|
|
60
|
+
cacheObjectStore.createIndex("partitionKey", "partitionKey");
|
|
61
|
+
cacheObjectStore.createIndex("fileId", "fileId");
|
|
62
|
+
},
|
|
63
|
+
blocked: () => {
|
|
64
|
+
reject(
|
|
65
|
+
new Error(
|
|
66
|
+
"Could not open DB since it is blocked by an older client that has the DB open",
|
|
67
|
+
),
|
|
68
|
+
);
|
|
69
|
+
},
|
|
70
|
+
}).then(resolve, reject);
|
|
71
|
+
});
|
|
88
72
|
}
|
|
89
73
|
|
|
90
74
|
// Deletes the indexed DB instance.
|
|
91
75
|
// Warning this can throw an error in Firefox incognito, where accessing storage is prohibited.
|
|
92
76
|
export function deleteFluidCacheIndexDbInstance(): Promise<void> {
|
|
93
|
-
|
|
77
|
+
return deleteDB(FluidDriverCacheDBName);
|
|
94
78
|
}
|
|
95
79
|
|
|
96
80
|
/**
|
|
97
81
|
* Schema for the object store used to cache driver information
|
|
98
82
|
*/
|
|
99
83
|
export interface FluidCacheDBSchema extends DBSchema {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
84
|
+
[FluidDriverObjectStoreName]: {
|
|
85
|
+
/**
|
|
86
|
+
* A unique identifier for an item in the cache. It is a combination of file, type, and cacheItemId
|
|
87
|
+
*/
|
|
88
|
+
key: string;
|
|
89
|
+
|
|
90
|
+
value: {
|
|
91
|
+
/**
|
|
92
|
+
* The identifier of the file associated with the cache entry
|
|
93
|
+
*/
|
|
94
|
+
fileId: string;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Describes the type of content being cached, such as snapshot
|
|
98
|
+
*/
|
|
99
|
+
type: string;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Files may have multiple cached items associated with them,
|
|
103
|
+
* this property uniquely identifies a specific cache entry for a file.
|
|
104
|
+
* This is not globally unique, but rather a unique id for this file
|
|
105
|
+
*/
|
|
106
|
+
cacheItemId: string;
|
|
107
|
+
|
|
108
|
+
/*
|
|
109
|
+
* Opaque object that the driver asks us to store in a cache for performance reasons
|
|
110
|
+
*/
|
|
111
|
+
cachedObject: any;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* A string to specify what partition of the cache you wish to use (e.g. a user id).
|
|
115
|
+
* Null can be used to explicity indicate no partitioning.
|
|
116
|
+
*/
|
|
117
|
+
// eslint-disable-next-line @rushstack/no-new-null
|
|
118
|
+
partitionKey: string | null;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The time when the cache entry was put into the cache
|
|
122
|
+
*/
|
|
123
|
+
createdTimeMs: number;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The last time the cache entry was used.
|
|
127
|
+
* This is initially set to the time the cache entry was created Measured as ms since unix epoch.
|
|
128
|
+
*/
|
|
129
|
+
lastAccessTimeMs: number;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
indexes: {
|
|
133
|
+
createdTimeMs: number;
|
|
134
|
+
partitionKey: string;
|
|
135
|
+
lastAccessTimeMs: number;
|
|
136
|
+
fileId: string;
|
|
137
|
+
};
|
|
138
|
+
};
|
|
155
139
|
}
|
|
@@ -4,18 +4,18 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
export const enum FluidCacheGenericEvent {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
"FluidCacheStorageInfo" = "FluidCacheStorageInfo",
|
|
8
|
+
"FluidCachePartitionKeyMismatch" = "FluidCachePartitionKeyMismatch",
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export const enum FluidCacheErrorEvent {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
"FluidCacheDeleteOldEntriesError" = "FluidCacheDeleteOldEntriesError",
|
|
13
|
+
"FluidCacheGetError" = "FluidCacheGetError",
|
|
14
|
+
"FluidCachePutError" = "FluidCachePutError",
|
|
15
|
+
"FluidCacheUpdateUsageError" = "FluidCacheUpdateUsageError",
|
|
16
|
+
"FluidCacheDeleteOldDbError" = "FluidCacheDeleteOldDbError",
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const enum FluidCacheEventSubCategories {
|
|
20
|
-
|
|
20
|
+
"FluidCache" = "FluidCache",
|
|
21
21
|
}
|
package/src/packageVersion.ts
CHANGED
package/src/scheduleIdleTask.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
interface TaskQueueItem {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/** The task to run */
|
|
8
|
+
task: () => void;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
// A set of tasks that still have to be run
|
|
@@ -20,34 +20,34 @@ let idleTaskScheduled = false;
|
|
|
20
20
|
* @param options - Optional configuration for the task execution
|
|
21
21
|
*/
|
|
22
22
|
export function scheduleIdleTask(task: () => void) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
taskQueue.push({
|
|
24
|
+
task,
|
|
25
|
+
});
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
ensureIdleCallback();
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Ensures an idle callback has been scheduled for the remaining tasks
|
|
32
32
|
*/
|
|
33
33
|
function ensureIdleCallback() {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
34
|
+
if (!idleTaskScheduled) {
|
|
35
|
+
// Exception added when eslint rule was added, this should be revisited when modifying this code
|
|
36
|
+
if (window.requestIdleCallback) {
|
|
37
|
+
window.requestIdleCallback(idleTaskCallback);
|
|
38
|
+
} else {
|
|
39
|
+
const deadline = Date.now() + 50;
|
|
40
|
+
window.setTimeout(
|
|
41
|
+
() =>
|
|
42
|
+
idleTaskCallback({
|
|
43
|
+
timeRemaining: () => Math.max(deadline - Date.now(), 0),
|
|
44
|
+
didTimeout: false,
|
|
45
|
+
}),
|
|
46
|
+
0,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
idleTaskScheduled = true;
|
|
50
|
+
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/**
|
|
@@ -58,43 +58,40 @@ function ensureIdleCallback() {
|
|
|
58
58
|
* we have enough time to continue running tasks. If omitted, we don't stop running tasks.
|
|
59
59
|
*/
|
|
60
60
|
function runTasks(
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
filter?: (taskQueueItem: TaskQueueItem) => boolean,
|
|
62
|
+
shouldContinueRunning?: () => boolean,
|
|
63
63
|
) {
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
// The next value for the task queue
|
|
65
|
+
const newTaskQueue: TaskQueueItem[] = [];
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
for (let index = 0; index < taskQueue.length; index += 1) {
|
|
68
|
+
if (shouldContinueRunning && !shouldContinueRunning()) {
|
|
69
|
+
// Add the tasks we didn't get to to the end of the new task queue
|
|
70
|
+
newTaskQueue.push(...taskQueue.slice(index));
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
const taskQueueItem = taskQueue[index];
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
if (filter && !filter(taskQueueItem)) {
|
|
77
|
+
newTaskQueue.push(taskQueueItem);
|
|
78
|
+
} else {
|
|
79
|
+
taskQueueItem.task();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
taskQueue = newTaskQueue;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
// Runs all the tasks in the task queue
|
|
87
|
-
function idleTaskCallback(deadline: {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const minTaskTime = 10;
|
|
93
|
-
runTasks(undefined, () => deadline.timeRemaining() > minTaskTime);
|
|
94
|
-
idleTaskScheduled = false;
|
|
87
|
+
function idleTaskCallback(deadline: { timeRemaining: () => number; readonly didTimeout: boolean }) {
|
|
88
|
+
// Minimum time that must be available on deadline to run any more tasks
|
|
89
|
+
const minTaskTime = 10;
|
|
90
|
+
runTasks(undefined, () => deadline.timeRemaining() > minTaskTime);
|
|
91
|
+
idleTaskScheduled = false;
|
|
95
92
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
// If we didn't run through the entire queue, schedule another idle callback
|
|
94
|
+
if (taskQueue.length > 0) {
|
|
95
|
+
ensureIdleCallback();
|
|
96
|
+
}
|
|
100
97
|
}
|
package/tsconfig.esnext.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "./lib",
|
|
5
|
+
"module": "esnext",
|
|
6
|
+
},
|
|
7
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"include": [
|
|
13
|
-
"src/**/*"
|
|
14
|
-
]
|
|
15
|
-
}
|
|
2
|
+
"extends": "@fluidframework/build-common/ts-common-config.json",
|
|
3
|
+
"exclude": ["src/test/**/*"],
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"composite": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
},
|
|
10
|
+
"include": ["src/**/*"],
|
|
11
|
+
}
|