@fluidframework/driver-web-cache 1.4.0-115997 → 2.0.0-dev-rc.1.0.0.224419
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/CHANGELOG.md +81 -0
- package/README.md +49 -18
- package/api-extractor-lint.json +4 -0
- package/api-extractor.json +2 -2
- package/api-report/driver-web-cache.api.md +37 -0
- package/dist/{FluidCache.js → FluidCache.cjs} +107 -46
- package/dist/FluidCache.cjs.map +1 -0
- package/dist/FluidCache.d.ts +18 -2
- package/dist/FluidCache.d.ts.map +1 -1
- package/dist/{FluidCacheIndexedDb.js → FluidCacheIndexedDb.cjs} +11 -7
- package/dist/FluidCacheIndexedDb.cjs.map +1 -0
- package/dist/FluidCacheIndexedDb.d.ts +11 -3
- package/dist/FluidCacheIndexedDb.d.ts.map +1 -1
- package/dist/driver-web-cache-alpha.d.ts +65 -0
- package/dist/driver-web-cache-beta.d.ts +19 -0
- package/dist/driver-web-cache-public.d.ts +19 -0
- package/dist/driver-web-cache-untrimmed.d.ts +65 -0
- package/dist/{fluidCacheTelemetry.js → fluidCacheTelemetry.cjs} +1 -1
- package/dist/fluidCacheTelemetry.cjs.map +1 -0
- package/dist/fluidCacheTelemetry.d.ts.map +1 -1
- package/dist/index.cjs +12 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/{packageVersion.js → packageVersion.cjs} +2 -2
- package/dist/packageVersion.cjs.map +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/{scheduleIdleTask.js → scheduleIdleTask.cjs} +7 -7
- package/dist/scheduleIdleTask.cjs.map +1 -0
- package/dist/tsdoc-metadata.json +11 -0
- package/jest.config.js +15 -5
- package/lib/{FluidCache.d.ts → FluidCache.d.mts} +18 -2
- package/lib/FluidCache.d.mts.map +1 -0
- package/lib/{FluidCache.js → FluidCache.mjs} +108 -47
- package/lib/FluidCache.mjs.map +1 -0
- package/lib/{FluidCacheIndexedDb.d.ts → FluidCacheIndexedDb.d.mts} +11 -3
- package/lib/FluidCacheIndexedDb.d.mts.map +1 -0
- package/lib/{FluidCacheIndexedDb.js → FluidCacheIndexedDb.mjs} +12 -8
- package/lib/FluidCacheIndexedDb.mjs.map +1 -0
- package/lib/driver-web-cache-alpha.d.mts +65 -0
- package/lib/driver-web-cache-beta.d.mts +19 -0
- package/lib/driver-web-cache-public.d.mts +19 -0
- package/lib/driver-web-cache-untrimmed.d.mts +65 -0
- package/lib/{fluidCacheTelemetry.d.ts.map → fluidCacheTelemetry.d.mts.map} +1 -1
- package/lib/{fluidCacheTelemetry.js → fluidCacheTelemetry.mjs} +1 -1
- package/lib/fluidCacheTelemetry.mjs.map +1 -0
- package/lib/{index.d.ts → index.d.mts} +2 -2
- package/lib/index.d.mts.map +1 -0
- package/lib/{index.js → index.mjs} +3 -3
- package/lib/index.mjs.map +1 -0
- package/lib/{packageVersion.d.ts → packageVersion.d.mts} +1 -1
- package/lib/{packageVersion.d.ts.map → packageVersion.d.mts.map} +1 -1
- package/lib/{packageVersion.js → packageVersion.mjs} +2 -2
- package/lib/packageVersion.mjs.map +1 -0
- package/lib/{scheduleIdleTask.js → scheduleIdleTask.mjs} +7 -7
- package/lib/scheduleIdleTask.mjs.map +1 -0
- package/package.json +78 -46
- package/prettier.config.cjs +8 -0
- package/src/FluidCache.ts +293 -253
- package/src/FluidCacheIndexedDb.ts +114 -122
- package/src/fluidCacheTelemetry.ts +8 -8
- package/src/index.ts +1 -1
- package/src/packageVersion.ts +1 -1
- package/src/scheduleIdleTask.ts +51 -54
- package/tsc-multi.test.json +4 -0
- package/tsconfig.json +12 -14
- package/dist/FluidCache.js.map +0 -1
- package/dist/FluidCacheIndexedDb.js.map +0 -1
- package/dist/fluidCacheTelemetry.js.map +0 -1
- package/dist/index.js +0 -21
- package/dist/index.js.map +0 -1
- package/dist/packageVersion.js.map +0 -1
- package/dist/scheduleIdleTask.js.map +0 -1
- package/lib/FluidCache.d.ts.map +0 -1
- package/lib/FluidCache.js.map +0 -1
- package/lib/FluidCacheIndexedDb.d.ts.map +0 -1
- package/lib/FluidCacheIndexedDb.js.map +0 -1
- package/lib/fluidCacheTelemetry.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/packageVersion.js.map +0 -1
- package/lib/scheduleIdleTask.js.map +0 -1
- package/tsconfig.esnext.json +0 -7
- /package/lib/{fluidCacheTelemetry.d.ts → fluidCacheTelemetry.d.mts} +0 -0
- /package/lib/{scheduleIdleTask.d.ts → scheduleIdleTask.d.mts} +0 -0
- /package/lib/{scheduleIdleTask.d.ts.map → scheduleIdleTask.d.mts.map} +0 -0
package/.eslintrc.js
CHANGED
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
module.exports = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
7
|
+
extends: [require.resolve("@fluidframework/eslint-config-fluid/minimal"), "prettier"],
|
|
8
|
+
parserOptions: {
|
|
9
|
+
project: ["./tsconfig.json", "./src/test/tsconfig.json"],
|
|
10
|
+
},
|
|
11
|
+
rules: {
|
|
12
|
+
"@typescript-eslint/no-non-null-assertion": "off",
|
|
13
|
+
"@typescript-eslint/no-use-before-define": "off",
|
|
14
|
+
"@typescript-eslint/strict-boolean-expressions": "off",
|
|
15
|
+
"@typescript-eslint/promise-function-async": "off",
|
|
16
|
+
"@typescript-eslint/no-misused-promises": "off",
|
|
17
|
+
},
|
|
18
18
|
};
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# @fluidframework/driver-web-cache
|
|
2
|
+
|
|
3
|
+
## 2.0.0-internal.8.0.0
|
|
4
|
+
|
|
5
|
+
Dependency updates only.
|
|
6
|
+
|
|
7
|
+
## 2.0.0-internal.7.4.0
|
|
8
|
+
|
|
9
|
+
Dependency updates only.
|
|
10
|
+
|
|
11
|
+
## 2.0.0-internal.7.3.0
|
|
12
|
+
|
|
13
|
+
Dependency updates only.
|
|
14
|
+
|
|
15
|
+
## 2.0.0-internal.7.2.0
|
|
16
|
+
|
|
17
|
+
Dependency updates only.
|
|
18
|
+
|
|
19
|
+
## 2.0.0-internal.7.1.0
|
|
20
|
+
|
|
21
|
+
Dependency updates only.
|
|
22
|
+
|
|
23
|
+
## 2.0.0-internal.7.0.0
|
|
24
|
+
|
|
25
|
+
### Major Changes
|
|
26
|
+
|
|
27
|
+
- Minimum TypeScript version now 5.1.6 [871b3493dd](https://github.com/microsoft/FluidFramework/commits/871b3493dd0d7ea3a89be64998ceb6cb9021a04e)
|
|
28
|
+
|
|
29
|
+
The minimum supported TypeScript version for Fluid 2.0 clients is now 5.1.6.
|
|
30
|
+
|
|
31
|
+
## 2.0.0-internal.6.4.0
|
|
32
|
+
|
|
33
|
+
Dependency updates only.
|
|
34
|
+
|
|
35
|
+
## 2.0.0-internal.6.3.0
|
|
36
|
+
|
|
37
|
+
Dependency updates only.
|
|
38
|
+
|
|
39
|
+
## 2.0.0-internal.6.2.0
|
|
40
|
+
|
|
41
|
+
Dependency updates only.
|
|
42
|
+
|
|
43
|
+
## 2.0.0-internal.6.1.0
|
|
44
|
+
|
|
45
|
+
Dependency updates only.
|
|
46
|
+
|
|
47
|
+
## 2.0.0-internal.6.0.0
|
|
48
|
+
|
|
49
|
+
### Major Changes
|
|
50
|
+
|
|
51
|
+
- Upgraded typescript transpilation target to ES2020 [8abce8cdb4](https://github.com/microsoft/FluidFramework/commits/8abce8cdb4e2832fb6405fb44e393bef03d5648a)
|
|
52
|
+
|
|
53
|
+
Upgraded typescript transpilation target to ES2020. This is done in order to decrease the bundle sizes of Fluid Framework packages. This has provided size improvements across the board for ex. Loader, Driver, Runtime etc. Reduced bundle sizes helps to load lesser code in apps and hence also helps to improve the perf.If any app wants to target any older versions of browsers with which this target version is not compatible, then they can use packages like babel to transpile to a older target.
|
|
54
|
+
|
|
55
|
+
## 2.0.0-internal.5.4.0
|
|
56
|
+
|
|
57
|
+
Dependency updates only.
|
|
58
|
+
|
|
59
|
+
## 2.0.0-internal.5.3.0
|
|
60
|
+
|
|
61
|
+
Dependency updates only.
|
|
62
|
+
|
|
63
|
+
## 2.0.0-internal.5.2.0
|
|
64
|
+
|
|
65
|
+
Dependency updates only.
|
|
66
|
+
|
|
67
|
+
## 2.0.0-internal.5.1.0
|
|
68
|
+
|
|
69
|
+
Dependency updates only.
|
|
70
|
+
|
|
71
|
+
## 2.0.0-internal.5.0.0
|
|
72
|
+
|
|
73
|
+
Dependency updates only.
|
|
74
|
+
|
|
75
|
+
## 2.0.0-internal.4.4.0
|
|
76
|
+
|
|
77
|
+
Dependency updates only.
|
|
78
|
+
|
|
79
|
+
## 2.0.0-internal.4.1.0
|
|
80
|
+
|
|
81
|
+
Dependency updates only.
|
package/README.md
CHANGED
|
@@ -5,28 +5,49 @@ storing of user content on the user's machine in order to provide faster boot ex
|
|
|
5
5
|
containers more than once. This implementation has a dependency on indexeddb, so it is intended to only be used in a browser
|
|
6
6
|
context.
|
|
7
7
|
|
|
8
|
+
<!-- AUTO-GENERATED-CONTENT:START (README_DEPENDENCY_GUIDELINES_SECTION:includeHeading=TRUE) -->
|
|
9
|
+
|
|
10
|
+
<!-- prettier-ignore-start -->
|
|
11
|
+
<!-- NOTE: This section is automatically generated using @fluid-tools/markdown-magic. Do not update these generated contents directly. -->
|
|
12
|
+
|
|
13
|
+
## Using Fluid Framework libraries
|
|
14
|
+
|
|
15
|
+
When taking a dependency on a Fluid Framework library, we recommend using a `^` (caret) version range, such as `^1.3.4`.
|
|
16
|
+
While Fluid Framework libraries may use different ranges with interdependencies between other Fluid Framework libraries,
|
|
17
|
+
library consumers should always prefer `^`.
|
|
18
|
+
|
|
19
|
+
Note that when depending on a library version of the form `2.0.0-internal.x.y.z`, called the Fluid internal version scheme,
|
|
20
|
+
you must use a `>= <` dependency range (such as `>=2.0.0-internal.x.y.z <2.0.0-internal.w.0.0` where `w` is `x+1`).
|
|
21
|
+
Standard `^` and `~` ranges will not work as expected.
|
|
22
|
+
See the [@fluid-tools/version-tools](https://github.com/microsoft/FluidFramework/blob/main/build-tools/packages/version-tools/README.md)
|
|
23
|
+
package for more information including tools to convert between version schemes.
|
|
24
|
+
|
|
25
|
+
<!-- prettier-ignore-end -->
|
|
26
|
+
|
|
27
|
+
<!-- AUTO-GENERATED-CONTENT:END -->
|
|
28
|
+
|
|
8
29
|
## Usage
|
|
9
30
|
|
|
10
31
|
```typescript
|
|
11
|
-
import { FluidCache } from
|
|
32
|
+
import { FluidCache } from "@fluidframework/driver-web-cache";
|
|
12
33
|
|
|
13
34
|
new FluidCache({
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
35
|
+
partitionKey: userId,
|
|
36
|
+
logger,
|
|
37
|
+
maxCacheItemAge,
|
|
38
|
+
});
|
|
18
39
|
```
|
|
19
40
|
|
|
20
41
|
### Parameters
|
|
21
42
|
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
|
|
29
|
-
|
|
43
|
+
- `partitionKey` - Used to determine what partition of the cache is being used, and can prevent multiple users on the
|
|
44
|
+
same machine from sharing a snapshot cache. If you absolutely know that users will not share the cache,
|
|
45
|
+
can also be set to `null`. Currently optional, but is proposed to be required in the next major bump.
|
|
46
|
+
The recommendation is to use this key to differentiate users for the cache data.
|
|
47
|
+
- `logger` - An optional implementation of the logger contract where diagnostic data can be logged.
|
|
48
|
+
- `maxCacheItemAge` - The cache tracks a timestamp with each entry. This flag specifies the maximum age (in milliseconds)
|
|
49
|
+
for a cache entry to be used. This flag does not control when cached content is deleted since different scenarios and
|
|
50
|
+
applications may have different staleness thresholds for the same data.
|
|
30
51
|
|
|
31
52
|
## Clearing cache entries
|
|
32
53
|
|
|
@@ -41,16 +62,26 @@ are on point for ensuring responsible usage of the snapshot caching capability t
|
|
|
41
62
|
customer promises, such as clearing out storage when appropriate or disabling snapshot caching under certain circumstances,
|
|
42
63
|
such as when it is known the user is logged in to a public computer.
|
|
43
64
|
|
|
44
|
-
|
|
45
65
|
```typescript
|
|
46
|
-
import { deleteFluidCacheIndexDbInstance } from
|
|
66
|
+
import { deleteFluidCacheIndexDbInstance } from "@fluidframework/driver-web-cache";
|
|
47
67
|
|
|
48
|
-
|
|
68
|
+
// We put a catch here because Firefox Incognito will throw an error here. This is why we claim this method is a "best effort", since sometimes the browser won't let us access storage
|
|
49
69
|
deleteFluidCacheIndexDbInstance().catch(() => {});
|
|
50
70
|
```
|
|
51
71
|
|
|
72
|
+
<!-- AUTO-GENERATED-CONTENT:START (README_TRADEMARK_SECTION:includeHeading=TRUE) -->
|
|
73
|
+
|
|
74
|
+
<!-- prettier-ignore-start -->
|
|
75
|
+
<!-- NOTE: This section is automatically generated using @fluid-tools/markdown-magic. Do not update these generated contents directly. -->
|
|
76
|
+
|
|
52
77
|
## Trademark
|
|
53
78
|
|
|
54
|
-
This project may contain Microsoft trademarks or logos for Microsoft projects, products, or services.
|
|
55
|
-
|
|
79
|
+
This project may contain Microsoft trademarks or logos for Microsoft projects, products, or services.
|
|
80
|
+
|
|
81
|
+
Use of these trademarks or logos must follow Microsoft's [Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
|
|
82
|
+
|
|
56
83
|
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
|
|
84
|
+
|
|
85
|
+
<!-- prettier-ignore-end -->
|
|
86
|
+
|
|
87
|
+
<!-- AUTO-GENERATED-CONTENT:END -->
|
package/api-extractor.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
|
3
|
+
"extends": "../../../common/build/build-common/api-extractor-base.json"
|
|
4
4
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
## API Report File for "@fluidframework/driver-web-cache"
|
|
2
|
+
|
|
3
|
+
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
|
|
7
|
+
import { DeleteDBCallbacks } from 'idb';
|
|
8
|
+
import { ICacheEntry } from '@fluidframework/odsp-driver-definitions';
|
|
9
|
+
import { IFileEntry } from '@fluidframework/odsp-driver-definitions';
|
|
10
|
+
import { IPersistedCache } from '@fluidframework/odsp-driver-definitions';
|
|
11
|
+
import { ITelemetryBaseLogger } from '@fluidframework/core-interfaces';
|
|
12
|
+
|
|
13
|
+
// @alpha
|
|
14
|
+
export function deleteFluidCacheIndexDbInstance(deleteDBCallbacks?: DeleteDBCallbacks): Promise<void>;
|
|
15
|
+
|
|
16
|
+
// @alpha
|
|
17
|
+
export class FluidCache implements IPersistedCache {
|
|
18
|
+
constructor(config: FluidCacheConfig);
|
|
19
|
+
// (undocumented)
|
|
20
|
+
get(cacheEntry: ICacheEntry): Promise<any>;
|
|
21
|
+
// (undocumented)
|
|
22
|
+
put(entry: ICacheEntry, value: any): Promise<void>;
|
|
23
|
+
// (undocumented)
|
|
24
|
+
removeEntries(file: IFileEntry): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// @alpha (undocumented)
|
|
28
|
+
export interface FluidCacheConfig {
|
|
29
|
+
closeDbAfterMs?: number;
|
|
30
|
+
logger?: ITelemetryBaseLogger;
|
|
31
|
+
maxCacheItemAge: number;
|
|
32
|
+
partitionKey: string | null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// (No @packageDocumentation comment for this package)
|
|
36
|
+
|
|
37
|
+
```
|
|
@@ -5,61 +5,121 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.FluidCache = void 0;
|
|
8
|
+
const core_utils_1 = require("@fluidframework/core-utils");
|
|
8
9
|
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
9
|
-
const scheduleIdleTask_1 = require("./scheduleIdleTask");
|
|
10
|
-
const FluidCacheIndexedDb_1 = require("./FluidCacheIndexedDb");
|
|
10
|
+
const scheduleIdleTask_1 = require("./scheduleIdleTask.cjs");
|
|
11
|
+
const FluidCacheIndexedDb_1 = require("./FluidCacheIndexedDb.cjs");
|
|
12
|
+
const packageVersion_1 = require("./packageVersion.cjs");
|
|
11
13
|
/**
|
|
12
|
-
* A cache that can be used by the Fluid ODSP driver to cache data for faster performance
|
|
14
|
+
* A cache that can be used by the Fluid ODSP driver to cache data for faster performance.
|
|
15
|
+
* @alpha
|
|
13
16
|
*/
|
|
14
17
|
class FluidCache {
|
|
15
18
|
constructor(config) {
|
|
16
|
-
this.
|
|
19
|
+
this.closeDbImmediately = true;
|
|
20
|
+
this.dbReuseCount = -1;
|
|
21
|
+
this.logger = (0, telemetry_utils_1.createChildLogger)({ logger: config.logger });
|
|
17
22
|
this.partitionKey = config.partitionKey;
|
|
18
23
|
this.maxCacheItemAge = config.maxCacheItemAge;
|
|
24
|
+
this.closeDbAfterMs = config.closeDbAfterMs ?? 0;
|
|
25
|
+
if (this.closeDbAfterMs > 0) {
|
|
26
|
+
this.closeDbImmediately = false;
|
|
27
|
+
}
|
|
19
28
|
(0, scheduleIdleTask_1.scheduleIdleTask)(async () => {
|
|
20
|
-
var _a;
|
|
21
29
|
// Log how much storage space is currently being used by indexed db.
|
|
22
30
|
// NOTE: This API is not supported in all browsers and it doesn't let you see the size of a specific DB.
|
|
23
31
|
// Exception added when eslint rule was added, this should be revisited when modifying this code
|
|
24
|
-
if (
|
|
32
|
+
if (navigator.storage?.estimate) {
|
|
25
33
|
const estimate = await navigator.storage.estimate();
|
|
26
34
|
// Some browsers have a usageDetails property that will tell you
|
|
27
35
|
// more detailed information on how the storage is being used
|
|
28
36
|
let indexedDBSize;
|
|
29
37
|
if ("usageDetails" in estimate) {
|
|
30
|
-
indexedDBSize = estimate
|
|
31
|
-
.
|
|
38
|
+
indexedDBSize = estimate.usageDetails
|
|
39
|
+
.indexedDB;
|
|
32
40
|
}
|
|
33
41
|
this.logger.sendTelemetryEvent({
|
|
34
|
-
eventName: "FluidCacheStorageInfo" /* FluidCacheStorageInfo */,
|
|
35
|
-
subCategory: "FluidCache" /* FluidCache */,
|
|
42
|
+
eventName: "FluidCacheStorageInfo" /* FluidCacheGenericEvent.FluidCacheStorageInfo */,
|
|
43
|
+
subCategory: "FluidCache" /* FluidCacheEventSubCategories.FluidCache */,
|
|
36
44
|
quota: estimate.quota,
|
|
37
45
|
usage: estimate.usage,
|
|
38
46
|
indexedDBSize,
|
|
47
|
+
pkgVersion: packageVersion_1.pkgVersion,
|
|
39
48
|
});
|
|
40
49
|
}
|
|
41
50
|
});
|
|
42
51
|
(0, scheduleIdleTask_1.scheduleIdleTask)(async () => {
|
|
52
|
+
let db;
|
|
43
53
|
// Delete entries that have not been accessed recently to clean up space
|
|
44
54
|
try {
|
|
45
|
-
|
|
55
|
+
db = await (0, FluidCacheIndexedDb_1.getFluidCacheIndexedDbInstance)(this.logger);
|
|
46
56
|
const transaction = db.transaction(FluidCacheIndexedDb_1.FluidDriverObjectStoreName, "readwrite");
|
|
47
|
-
const index = transaction.store.index("
|
|
48
|
-
// Get items
|
|
49
|
-
const keysToDelete = await index.getAllKeys(IDBKeyRange.upperBound(new Date().getTime() -
|
|
57
|
+
const index = transaction.store.index("createdTimeMs");
|
|
58
|
+
// Get items which were cached before the maxCacheItemAge.
|
|
59
|
+
const keysToDelete = await index.getAllKeys(IDBKeyRange.upperBound(new Date().getTime() - this.maxCacheItemAge));
|
|
50
60
|
await Promise.all(keysToDelete.map((key) => transaction.store.delete(key)));
|
|
51
61
|
await transaction.done;
|
|
52
62
|
}
|
|
53
63
|
catch (error) {
|
|
54
64
|
this.logger.sendErrorEvent({
|
|
55
|
-
eventName: "FluidCacheDeleteOldEntriesError" /* FluidCacheDeleteOldEntriesError */,
|
|
65
|
+
eventName: "FluidCacheDeleteOldEntriesError" /* FluidCacheErrorEvent.FluidCacheDeleteOldEntriesError */,
|
|
66
|
+
pkgVersion: packageVersion_1.pkgVersion,
|
|
56
67
|
}, error);
|
|
57
68
|
}
|
|
69
|
+
finally {
|
|
70
|
+
db?.close();
|
|
71
|
+
}
|
|
58
72
|
});
|
|
59
73
|
}
|
|
74
|
+
async openDb() {
|
|
75
|
+
if (this.closeDbImmediately) {
|
|
76
|
+
return (0, FluidCacheIndexedDb_1.getFluidCacheIndexedDbInstance)(this.logger);
|
|
77
|
+
}
|
|
78
|
+
if (this.db === undefined) {
|
|
79
|
+
const dbInstance = await (0, FluidCacheIndexedDb_1.getFluidCacheIndexedDbInstance)(this.logger);
|
|
80
|
+
if (this.db === undefined) {
|
|
81
|
+
// Reset the counter on first open.
|
|
82
|
+
this.dbReuseCount = -1;
|
|
83
|
+
this.db = dbInstance;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
dbInstance.close();
|
|
87
|
+
this.dbReuseCount += 1;
|
|
88
|
+
return this.db;
|
|
89
|
+
}
|
|
90
|
+
// Need to close the db on version change if opened.
|
|
91
|
+
this.db.onversionchange = (ev) => {
|
|
92
|
+
this.db?.close();
|
|
93
|
+
this.db = undefined;
|
|
94
|
+
clearTimeout(this.dbCloseTimer);
|
|
95
|
+
this.dbCloseTimer = undefined;
|
|
96
|
+
};
|
|
97
|
+
this.db.addEventListener("close", (ev) => {
|
|
98
|
+
clearTimeout(this.dbCloseTimer);
|
|
99
|
+
this.dbCloseTimer = undefined;
|
|
100
|
+
this.db = undefined;
|
|
101
|
+
});
|
|
102
|
+
// Schedule db close after this.closeDbAfterMs.
|
|
103
|
+
(0, core_utils_1.assert)(this.dbCloseTimer === undefined, 0x6c6 /* timer should not be set yet!! */);
|
|
104
|
+
this.dbCloseTimer = setTimeout(() => {
|
|
105
|
+
this.db?.close();
|
|
106
|
+
this.db = undefined;
|
|
107
|
+
this.dbCloseTimer = undefined;
|
|
108
|
+
}, this.closeDbAfterMs);
|
|
109
|
+
}
|
|
110
|
+
(0, core_utils_1.assert)(this.db !== undefined, 0x6c7 /* db should be intialized by now */);
|
|
111
|
+
this.dbReuseCount += 1;
|
|
112
|
+
return this.db;
|
|
113
|
+
}
|
|
114
|
+
closeDb(db) {
|
|
115
|
+
if (this.closeDbImmediately) {
|
|
116
|
+
db?.close();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
60
119
|
async removeEntries(file) {
|
|
120
|
+
let db;
|
|
61
121
|
try {
|
|
62
|
-
|
|
122
|
+
db = await this.openDb();
|
|
63
123
|
const transaction = db.transaction(FluidCacheIndexedDb_1.FluidDriverObjectStoreName, "readwrite");
|
|
64
124
|
const index = transaction.store.index("fileId");
|
|
65
125
|
const keysToDelete = await index.getAllKeys(file.docId);
|
|
@@ -68,9 +128,13 @@ class FluidCache {
|
|
|
68
128
|
}
|
|
69
129
|
catch (error) {
|
|
70
130
|
this.logger.sendErrorEvent({
|
|
71
|
-
eventName: "FluidCacheDeleteOldEntriesError" /* FluidCacheDeleteOldEntriesError */,
|
|
131
|
+
eventName: "FluidCacheDeleteOldEntriesError" /* FluidCacheErrorEvent.FluidCacheDeleteOldEntriesError */,
|
|
132
|
+
pkgVersion: packageVersion_1.pkgVersion,
|
|
72
133
|
}, error);
|
|
73
134
|
}
|
|
135
|
+
finally {
|
|
136
|
+
this.closeDb(db);
|
|
137
|
+
}
|
|
74
138
|
}
|
|
75
139
|
async get(cacheEntry) {
|
|
76
140
|
const startTime = performance.now();
|
|
@@ -80,63 +144,57 @@ class FluidCache {
|
|
|
80
144
|
cacheHit: cachedItem !== undefined,
|
|
81
145
|
type: cacheEntry.type,
|
|
82
146
|
duration: performance.now() - startTime,
|
|
147
|
+
dbOpenPerf: cachedItem?.dbOpenPerf,
|
|
148
|
+
dbReuseCount: this.dbReuseCount,
|
|
149
|
+
pkgVersion: packageVersion_1.pkgVersion,
|
|
83
150
|
});
|
|
84
151
|
// Value will contain metadata like the expiry time, we just want to return the object we were asked to cache
|
|
85
152
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
86
|
-
return cachedItem
|
|
153
|
+
return cachedItem?.cachedObject;
|
|
87
154
|
}
|
|
88
155
|
async getItemFromCache(cacheEntry) {
|
|
156
|
+
let db;
|
|
89
157
|
try {
|
|
90
158
|
const key = (0, FluidCacheIndexedDb_1.getKeyForCacheEntry)(cacheEntry);
|
|
91
|
-
const
|
|
159
|
+
const dbOpenStartTime = performance.now();
|
|
160
|
+
db = await this.openDb();
|
|
161
|
+
const dbOpenPerf = performance.now() - dbOpenStartTime;
|
|
92
162
|
const value = await db.get(FluidCacheIndexedDb_1.FluidDriverObjectStoreName, key);
|
|
93
163
|
if (!value) {
|
|
164
|
+
this.closeDb(db);
|
|
94
165
|
return undefined;
|
|
95
166
|
}
|
|
96
167
|
// If the data does not come from the same partition, don't return it
|
|
97
168
|
if (value.partitionKey !== this.partitionKey) {
|
|
98
169
|
this.logger.sendTelemetryEvent({
|
|
99
|
-
eventName: "FluidCachePartitionKeyMismatch" /* FluidCachePartitionKeyMismatch */,
|
|
100
|
-
subCategory: "FluidCache" /* FluidCache */,
|
|
170
|
+
eventName: "FluidCachePartitionKeyMismatch" /* FluidCacheGenericEvent.FluidCachePartitionKeyMismatch */,
|
|
171
|
+
subCategory: "FluidCache" /* FluidCacheEventSubCategories.FluidCache */,
|
|
172
|
+
pkgVersion: packageVersion_1.pkgVersion,
|
|
101
173
|
});
|
|
174
|
+
this.closeDb(db);
|
|
102
175
|
return undefined;
|
|
103
176
|
}
|
|
104
177
|
const currentTime = new Date().getTime();
|
|
105
178
|
// If too much time has passed since this cache entry was used, we will also return undefined
|
|
106
179
|
if (currentTime - value.createdTimeMs > this.maxCacheItemAge) {
|
|
180
|
+
this.closeDb(db);
|
|
107
181
|
return undefined;
|
|
108
182
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
// We catch this promise because there is no user bad if this is rejected.
|
|
112
|
-
transaction.store
|
|
113
|
-
.get(key)
|
|
114
|
-
.then(async (valueToUpdate) => {
|
|
115
|
-
// This value in the database could have been updated concurrently by other tabs/iframes
|
|
116
|
-
// since we first read it. Only update the last accessed time if the current value in the
|
|
117
|
-
// DB was the same one we returned.
|
|
118
|
-
if (valueToUpdate !== undefined &&
|
|
119
|
-
valueToUpdate.createdTimeMs === value.createdTimeMs &&
|
|
120
|
-
(valueToUpdate.lastAccessTimeMs === undefined ||
|
|
121
|
-
valueToUpdate.lastAccessTimeMs < currentTime)) {
|
|
122
|
-
await transaction.store.put(Object.assign(Object.assign({}, valueToUpdate), { lastAccessTimeMs: currentTime }), key);
|
|
123
|
-
}
|
|
124
|
-
await transaction.done;
|
|
125
|
-
db.close();
|
|
126
|
-
})
|
|
127
|
-
.catch(() => { });
|
|
128
|
-
return value;
|
|
183
|
+
this.closeDb(db);
|
|
184
|
+
return { ...value, dbOpenPerf };
|
|
129
185
|
}
|
|
130
186
|
catch (error) {
|
|
131
187
|
// We can fail to open the db for a variety of reasons,
|
|
132
188
|
// such as the database version having upgraded underneath us. Return undefined in this case
|
|
133
|
-
this.logger.sendErrorEvent({ eventName: "FluidCacheGetError" /* FluidCacheGetError
|
|
189
|
+
this.logger.sendErrorEvent({ eventName: "FluidCacheGetError" /* FluidCacheErrorEvent.FluidCacheGetError */, pkgVersion: packageVersion_1.pkgVersion }, error);
|
|
190
|
+
this.closeDb(db);
|
|
134
191
|
return undefined;
|
|
135
192
|
}
|
|
136
193
|
}
|
|
137
194
|
async put(entry, value) {
|
|
195
|
+
let db;
|
|
138
196
|
try {
|
|
139
|
-
|
|
197
|
+
db = await this.openDb();
|
|
140
198
|
const currentTime = new Date().getTime();
|
|
141
199
|
await db.put(FluidCacheIndexedDb_1.FluidDriverObjectStoreName, {
|
|
142
200
|
cachedObject: value,
|
|
@@ -147,14 +205,17 @@ class FluidCache {
|
|
|
147
205
|
createdTimeMs: currentTime,
|
|
148
206
|
lastAccessTimeMs: currentTime,
|
|
149
207
|
}, (0, FluidCacheIndexedDb_1.getKeyForCacheEntry)(entry));
|
|
150
|
-
|
|
208
|
+
this.closeDb(db);
|
|
151
209
|
}
|
|
152
210
|
catch (error) {
|
|
153
211
|
// We can fail to open the db for a variety of reasons,
|
|
154
212
|
// such as the database version having upgraded underneath us
|
|
155
|
-
this.logger.sendErrorEvent({ eventName: "FluidCachePutError" /* FluidCachePutError
|
|
213
|
+
this.logger.sendErrorEvent({ eventName: "FluidCachePutError" /* FluidCacheErrorEvent.FluidCachePutError */, pkgVersion: packageVersion_1.pkgVersion }, error);
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
this.closeDb(db);
|
|
156
217
|
}
|
|
157
218
|
}
|
|
158
219
|
}
|
|
159
220
|
exports.FluidCache = FluidCache;
|
|
160
|
-
//# sourceMappingURL=FluidCache.
|
|
221
|
+
//# sourceMappingURL=FluidCache.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FluidCache.cjs","sourceRoot":"","sources":["../src/FluidCache.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAGH,2DAAoD;AAGpD,qEAAyF;AACzF,6DAAsD;AACtD,mEAK+B;AAM/B,yDAA8C;AAwC9C;;;GAGG;AACH,MAAa,UAAU;IAYtB,YAAY,MAAwB;QANnB,uBAAkB,GAAY,IAAI,CAAC;QAI5C,iBAAY,GAAW,CAAC,CAAC,CAAC;QAGjC,IAAI,CAAC,MAAM,GAAG,IAAA,mCAAiB,EAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAC3D,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;QACxC,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,eAAe,CAAC;QAC9C,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc,IAAI,CAAC,CAAC;QACjD,IAAI,IAAI,CAAC,cAAc,GAAG,CAAC,EAAE;YAC5B,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;SAChC;QAED,IAAA,mCAAgB,EAAC,KAAK,IAAI,EAAE;YAC3B,oEAAoE;YACpE,wGAAwG;YACxG,gGAAgG;YAChG,IAAI,SAAS,CAAC,OAAO,EAAE,QAAQ,EAAE;gBAChC,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;gBAEpD,gEAAgE;gBAChE,6DAA6D;gBAC7D,IAAI,aAAiC,CAAC;gBACtC,IAAI,cAAc,IAAI,QAAQ,EAAE;oBAC/B,aAAa,GAAK,QAAgB,CAAC,YAAyC;yBAC1E,SAAS,CAAC;iBACZ;gBAED,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC;oBAC9B,SAAS,4EAA8C;oBACvD,WAAW,4DAAyC;oBACpD,KAAK,EAAE,QAAQ,CAAC,KAAK;oBACrB,KAAK,EAAE,QAAQ,CAAC,KAAK;oBACrB,aAAa;oBACb,UAAU,EAAV,2BAAU;iBACV,CAAC,CAAC;aACH;QACF,CAAC,CAAC,CAAC;QAEH,IAAA,mCAAgB,EAAC,KAAK,IAAI,EAAE;YAC3B,IAAI,EAAgD,CAAC;YAErD,wEAAwE;YACxE,IAAI;gBACH,EAAE,GAAG,MAAM,IAAA,oDAA8B,EAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAEvD,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,gDAA0B,EAAE,WAAW,CAAC,CAAC;gBAC5E,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;gBACvD,0DAA0D;gBAC1D,MAAM,YAAY,GAAG,MAAM,KAAK,CAAC,UAAU,CAC1C,WAAW,CAAC,UAAU,CAAC,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,CACnE,CAAC;gBAEF,MAAM,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBAC5E,MAAM,WAAW,CAAC,IAAI,CAAC;aACvB;YAAC,OAAO,KAAU,EAAE;gBACpB,IAAI,CAAC,MAAM,CAAC,cAAc,CACzB;oBACC,SAAS,8FAAsD;oBAC/D,UAAU,EAAV,2BAAU;iBACV,EACD,KAAK,CACL,CAAC;aACF;oBAAS;gBACT,EAAE,EAAE,KAAK,EAAE,CAAC;aACZ;QACF,CAAC,CAAC,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,MAAM;QACnB,IAAI,IAAI,CAAC,kBAAkB,EAAE;YAC5B,OAAO,IAAA,oDAA8B,EAAC,IAAI,CAAC,MAAM,CAAC,CAAC;SACnD;QACD,IAAI,IAAI,CAAC,EAAE,KAAK,SAAS,EAAE;YAC1B,MAAM,UAAU,GAAG,MAAM,IAAA,oDAA8B,EAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrE,IAAI,IAAI,CAAC,EAAE,KAAK,SAAS,EAAE;gBAC1B,mCAAmC;gBACnC,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;gBACvB,IAAI,CAAC,EAAE,GAAG,UAAU,CAAC;aACrB;iBAAM;gBACN,UAAU,CAAC,KAAK,EAAE,CAAC;gBACnB,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC;gBACvB,OAAO,IAAI,CAAC,EAAE,CAAC;aACf;YACD,oDAAoD;YACpD,IAAI,CAAC,EAAE,CAAC,eAAe,GAAG,CAAC,EAAE,EAAE,EAAE;gBAChC,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC;gBACjB,IAAI,CAAC,EAAE,GAAG,SAAS,CAAC;gBACpB,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBAChC,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;YAC/B,CAAC,CAAC;YACF,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE;gBACxC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBAChC,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;gBAC9B,IAAI,CAAC,EAAE,GAAG,SAAS,CAAC;YACrB,CAAC,CAAC,CAAC;YACH,+CAA+C;YAC/C,IAAA,mBAAM,EAAC,IAAI,CAAC,YAAY,KAAK,SAAS,EAAE,KAAK,CAAC,mCAAmC,CAAC,CAAC;YACnF,IAAI,CAAC,YAAY,GAAG,UAAU,CAAC,GAAG,EAAE;gBACnC,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC;gBACjB,IAAI,CAAC,EAAE,GAAG,SAAS,CAAC;gBACpB,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;YAC/B,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;SACxB;QACD,IAAA,mBAAM,EAAC,IAAI,CAAC,EAAE,KAAK,SAAS,EAAE,KAAK,CAAC,oCAAoC,CAAC,CAAC;QAC1E,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC,EAAE,CAAC;IAChB,CAAC;IAEO,OAAO,CAAC,EAAqC;QACpD,IAAI,IAAI,CAAC,kBAAkB,EAAE;YAC5B,EAAE,EAAE,KAAK,EAAE,CAAC;SACZ;IACF,CAAC;IAEM,KAAK,CAAC,aAAa,CAAC,IAAgB;QAC1C,IAAI,EAAgD,CAAC;QACrD,IAAI;YACH,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;YAEzB,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC,gDAA0B,EAAE,WAAW,CAAC,CAAC;YAC5E,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAEhD,MAAM,YAAY,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAExD,MAAM,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAC5E,MAAM,WAAW,CAAC,IAAI,CAAC;SACvB;QAAC,OAAO,KAAU,EAAE;YACpB,IAAI,CAAC,MAAM,CAAC,cAAc,CACzB;gBACC,SAAS,8FAAsD;gBAC/D,UAAU,EAAV,2BAAU;aACV,EACD,KAAK,CACL,CAAC;SACF;gBAAS;YACT,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;SACjB;IACF,CAAC;IAEM,KAAK,CAAC,GAAG,CAAC,UAAuB;QACvC,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAEpC,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;QAE3D,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;YAChC,SAAS,EAAE,kBAAkB;YAC7B,QAAQ,EAAE,UAAU,KAAK,SAAS;YAClC,IAAI,EAAE,UAAU,CAAC,IAAI;YACrB,QAAQ,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,SAAS;YACvC,UAAU,EAAE,UAAU,EAAE,UAAU;YAClC,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,UAAU,EAAV,2BAAU;SACV,CAAC,CAAC;QAEH,6GAA6G;QAC7G,+DAA+D;QAC/D,OAAO,UAAU,EAAE,YAAY,CAAC;IACjC,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAAC,UAAuB;QACrD,IAAI,EAAgD,CAAC;QACrD,IAAI;YACH,MAAM,GAAG,GAAG,IAAA,yCAAmB,EAAC,UAAU,CAAC,CAAC;YAE5C,MAAM,eAAe,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;YAC1C,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;YACzB,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,eAAe,CAAC;YACvD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,gDAA0B,EAAE,GAAG,CAAC,CAAC;YAE5D,IAAI,CAAC,KAAK,EAAE;gBACX,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBACjB,OAAO,SAAS,CAAC;aACjB;YAED,qEAAqE;YACrE,IAAI,KAAK,CAAC,YAAY,KAAK,IAAI,CAAC,YAAY,EAAE;gBAC7C,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC;oBAC9B,SAAS,8FAAuD;oBAChE,WAAW,4DAAyC;oBACpD,UAAU,EAAV,2BAAU;iBACV,CAAC,CAAC;gBAEH,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBACjB,OAAO,SAAS,CAAC;aACjB;YAED,MAAM,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;YAEzC,6FAA6F;YAC7F,IAAI,WAAW,GAAG,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,eAAe,EAAE;gBAC7D,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBACjB,OAAO,SAAS,CAAC;aACjB;YAED,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACjB,OAAO,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,CAAC;SAChC;QAAC,OAAO,KAAU,EAAE;YACpB,uDAAuD;YACvD,4FAA4F;YAC5F,IAAI,CAAC,MAAM,CAAC,cAAc,CACzB,EAAE,SAAS,oEAAyC,EAAE,UAAU,EAAV,2BAAU,EAAE,EAClE,KAAK,CACL,CAAC;YACF,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACjB,OAAO,SAAS,CAAC;SACjB;IACF,CAAC;IAEM,KAAK,CAAC,GAAG,CAAC,KAAkB,EAAE,KAAU;QAC9C,IAAI,EAAgD,CAAC;QACrD,IAAI;YACH,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;YAEzB,MAAM,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;YAEzC,MAAM,EAAE,CAAC,GAAG,CACX,gDAA0B,EAC1B;gBACC,YAAY,EAAE,KAAK;gBACnB,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK;gBACxB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,WAAW,EAAE,KAAK,CAAC,GAAG;gBACtB,YAAY,EAAE,IAAI,CAAC,YAAY;gBAC/B,aAAa,EAAE,WAAW;gBAC1B,gBAAgB,EAAE,WAAW;aAC7B,EACD,IAAA,yCAAmB,EAAC,KAAK,CAAC,CAC1B,CAAC;YACF,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;SACjB;QAAC,OAAO,KAAU,EAAE;YACpB,uDAAuD;YACvD,6DAA6D;YAC7D,IAAI,CAAC,MAAM,CAAC,cAAc,CACzB,EAAE,SAAS,oEAAyC,EAAE,UAAU,EAAV,2BAAU,EAAE,EAClE,KAAK,CACL,CAAC;SACF;gBAAS;YACT,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;SACjB;IACF,CAAC;CACD;AAzPD,gCAyPC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IDBPDatabase } from \"idb\";\nimport { assert } from \"@fluidframework/core-utils\";\nimport { IPersistedCache, ICacheEntry, IFileEntry } from \"@fluidframework/odsp-driver-definitions\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { ITelemetryLoggerExt, createChildLogger } from \"@fluidframework/telemetry-utils\";\nimport { scheduleIdleTask } from \"./scheduleIdleTask\";\nimport {\n\tgetFluidCacheIndexedDbInstance,\n\tFluidCacheDBSchema,\n\tFluidDriverObjectStoreName,\n\tgetKeyForCacheEntry,\n} from \"./FluidCacheIndexedDb\";\nimport {\n\tFluidCacheErrorEvent,\n\tFluidCacheEventSubCategories,\n\tFluidCacheGenericEvent,\n} from \"./fluidCacheTelemetry\";\nimport { pkgVersion } from \"./packageVersion\";\n\n// Some browsers have a usageDetails property that will tell you more detailed information\n// on how the storage is being used\ninterface StorageQuotaUsageDetails {\n\tindexedDB: number | undefined;\n}\n\n/**\n * @alpha\n */\nexport interface FluidCacheConfig {\n\t/**\n\t * A string to specify what partition of the cache you wish to use (e.g. a user id).\n\t * Null can be used to explicity indicate no partitioning, and has been chosen\n\t * vs undefined so that it is clear this is an intentional choice by the caller.\n\t * A null value should only be used when the host can ensure that the cache is not able\n\t * to be shared with multiple users.\n\t */\n\t// eslint-disable-next-line @rushstack/no-new-null\n\tpartitionKey: string | null;\n\n\t/**\n\t * A logger that can be used to get insight into cache performance and errors\n\t */\n\tlogger?: ITelemetryBaseLogger;\n\n\t/**\n\t * A value in milliseconds that determines the maximum age of a cache entry to return.\n\t * If an entry exists in the cache, but is older than this value, the cached value will not be returned.\n\t */\n\tmaxCacheItemAge: number;\n\n\t/**\n\t * Each time db is opened, it will remain open for this much time. To improve perf, if this property is set as\n\t * any number greater than 0, then db will not be closed immediately after usage. This value is in milliseconds.\n\t */\n\tcloseDbAfterMs?: number;\n}\n\n/**\n * A cache that can be used by the Fluid ODSP driver to cache data for faster performance.\n * @alpha\n */\nexport class FluidCache implements IPersistedCache {\n\tprivate readonly logger: ITelemetryLoggerExt;\n\n\tprivate readonly partitionKey: string | null;\n\n\tprivate readonly maxCacheItemAge: number;\n\tprivate readonly closeDbImmediately: boolean = true;\n\tprivate readonly closeDbAfterMs: number;\n\tprivate db: IDBPDatabase<FluidCacheDBSchema> | undefined;\n\tprivate dbCloseTimer: ReturnType<typeof setTimeout> | undefined;\n\tprivate dbReuseCount: number = -1;\n\n\tconstructor(config: FluidCacheConfig) {\n\t\tthis.logger = createChildLogger({ logger: config.logger });\n\t\tthis.partitionKey = config.partitionKey;\n\t\tthis.maxCacheItemAge = config.maxCacheItemAge;\n\t\tthis.closeDbAfterMs = config.closeDbAfterMs ?? 0;\n\t\tif (this.closeDbAfterMs > 0) {\n\t\t\tthis.closeDbImmediately = false;\n\t\t}\n\n\t\tscheduleIdleTask(async () => {\n\t\t\t// Log how much storage space is currently being used by indexed db.\n\t\t\t// NOTE: This API is not supported in all browsers and it doesn't let you see the size of a specific DB.\n\t\t\t// Exception added when eslint rule was added, this should be revisited when modifying this code\n\t\t\tif (navigator.storage?.estimate) {\n\t\t\t\tconst estimate = await navigator.storage.estimate();\n\n\t\t\t\t// Some browsers have a usageDetails property that will tell you\n\t\t\t\t// more detailed information on how the storage is being used\n\t\t\t\tlet indexedDBSize: number | undefined;\n\t\t\t\tif (\"usageDetails\" in estimate) {\n\t\t\t\t\tindexedDBSize = ((estimate as any).usageDetails as StorageQuotaUsageDetails)\n\t\t\t\t\t\t.indexedDB;\n\t\t\t\t}\n\n\t\t\t\tthis.logger.sendTelemetryEvent({\n\t\t\t\t\teventName: FluidCacheGenericEvent.FluidCacheStorageInfo,\n\t\t\t\t\tsubCategory: FluidCacheEventSubCategories.FluidCache,\n\t\t\t\t\tquota: estimate.quota,\n\t\t\t\t\tusage: estimate.usage,\n\t\t\t\t\tindexedDBSize,\n\t\t\t\t\tpkgVersion,\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\t\tscheduleIdleTask(async () => {\n\t\t\tlet db: IDBPDatabase<FluidCacheDBSchema> | undefined;\n\n\t\t\t// Delete entries that have not been accessed recently to clean up space\n\t\t\ttry {\n\t\t\t\tdb = await getFluidCacheIndexedDbInstance(this.logger);\n\n\t\t\t\tconst transaction = db.transaction(FluidDriverObjectStoreName, \"readwrite\");\n\t\t\t\tconst index = transaction.store.index(\"createdTimeMs\");\n\t\t\t\t// Get items which were cached before the maxCacheItemAge.\n\t\t\t\tconst keysToDelete = await index.getAllKeys(\n\t\t\t\t\tIDBKeyRange.upperBound(new Date().getTime() - this.maxCacheItemAge),\n\t\t\t\t);\n\n\t\t\t\tawait Promise.all(keysToDelete.map((key) => transaction.store.delete(key)));\n\t\t\t\tawait transaction.done;\n\t\t\t} catch (error: any) {\n\t\t\t\tthis.logger.sendErrorEvent(\n\t\t\t\t\t{\n\t\t\t\t\t\teventName: FluidCacheErrorEvent.FluidCacheDeleteOldEntriesError,\n\t\t\t\t\t\tpkgVersion,\n\t\t\t\t\t},\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t} finally {\n\t\t\t\tdb?.close();\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate async openDb() {\n\t\tif (this.closeDbImmediately) {\n\t\t\treturn getFluidCacheIndexedDbInstance(this.logger);\n\t\t}\n\t\tif (this.db === undefined) {\n\t\t\tconst dbInstance = await getFluidCacheIndexedDbInstance(this.logger);\n\t\t\tif (this.db === undefined) {\n\t\t\t\t// Reset the counter on first open.\n\t\t\t\tthis.dbReuseCount = -1;\n\t\t\t\tthis.db = dbInstance;\n\t\t\t} else {\n\t\t\t\tdbInstance.close();\n\t\t\t\tthis.dbReuseCount += 1;\n\t\t\t\treturn this.db;\n\t\t\t}\n\t\t\t// Need to close the db on version change if opened.\n\t\t\tthis.db.onversionchange = (ev) => {\n\t\t\t\tthis.db?.close();\n\t\t\t\tthis.db = undefined;\n\t\t\t\tclearTimeout(this.dbCloseTimer);\n\t\t\t\tthis.dbCloseTimer = undefined;\n\t\t\t};\n\t\t\tthis.db.addEventListener(\"close\", (ev) => {\n\t\t\t\tclearTimeout(this.dbCloseTimer);\n\t\t\t\tthis.dbCloseTimer = undefined;\n\t\t\t\tthis.db = undefined;\n\t\t\t});\n\t\t\t// Schedule db close after this.closeDbAfterMs.\n\t\t\tassert(this.dbCloseTimer === undefined, 0x6c6 /* timer should not be set yet!! */);\n\t\t\tthis.dbCloseTimer = setTimeout(() => {\n\t\t\t\tthis.db?.close();\n\t\t\t\tthis.db = undefined;\n\t\t\t\tthis.dbCloseTimer = undefined;\n\t\t\t}, this.closeDbAfterMs);\n\t\t}\n\t\tassert(this.db !== undefined, 0x6c7 /* db should be intialized by now */);\n\t\tthis.dbReuseCount += 1;\n\t\treturn this.db;\n\t}\n\n\tprivate closeDb(db?: IDBPDatabase<FluidCacheDBSchema>) {\n\t\tif (this.closeDbImmediately) {\n\t\t\tdb?.close();\n\t\t}\n\t}\n\n\tpublic async removeEntries(file: IFileEntry): Promise<void> {\n\t\tlet db: IDBPDatabase<FluidCacheDBSchema> | undefined;\n\t\ttry {\n\t\t\tdb = await this.openDb();\n\n\t\t\tconst transaction = db.transaction(FluidDriverObjectStoreName, \"readwrite\");\n\t\t\tconst index = transaction.store.index(\"fileId\");\n\n\t\t\tconst keysToDelete = await index.getAllKeys(file.docId);\n\n\t\t\tawait Promise.all(keysToDelete.map((key) => transaction.store.delete(key)));\n\t\t\tawait transaction.done;\n\t\t} catch (error: any) {\n\t\t\tthis.logger.sendErrorEvent(\n\t\t\t\t{\n\t\t\t\t\teventName: FluidCacheErrorEvent.FluidCacheDeleteOldEntriesError,\n\t\t\t\t\tpkgVersion,\n\t\t\t\t},\n\t\t\t\terror,\n\t\t\t);\n\t\t} finally {\n\t\t\tthis.closeDb(db);\n\t\t}\n\t}\n\n\tpublic async get(cacheEntry: ICacheEntry): Promise<any> {\n\t\tconst startTime = performance.now();\n\n\t\tconst cachedItem = await this.getItemFromCache(cacheEntry);\n\n\t\tthis.logger.sendPerformanceEvent({\n\t\t\teventName: \"FluidCacheAccess\",\n\t\t\tcacheHit: cachedItem !== undefined,\n\t\t\ttype: cacheEntry.type,\n\t\t\tduration: performance.now() - startTime,\n\t\t\tdbOpenPerf: cachedItem?.dbOpenPerf,\n\t\t\tdbReuseCount: this.dbReuseCount,\n\t\t\tpkgVersion,\n\t\t});\n\n\t\t// Value will contain metadata like the expiry time, we just want to return the object we were asked to cache\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-return\n\t\treturn cachedItem?.cachedObject;\n\t}\n\n\tprivate async getItemFromCache(cacheEntry: ICacheEntry) {\n\t\tlet db: IDBPDatabase<FluidCacheDBSchema> | undefined;\n\t\ttry {\n\t\t\tconst key = getKeyForCacheEntry(cacheEntry);\n\n\t\t\tconst dbOpenStartTime = performance.now();\n\t\t\tdb = await this.openDb();\n\t\t\tconst dbOpenPerf = performance.now() - dbOpenStartTime;\n\t\t\tconst value = await db.get(FluidDriverObjectStoreName, key);\n\n\t\t\tif (!value) {\n\t\t\t\tthis.closeDb(db);\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\t// If the data does not come from the same partition, don't return it\n\t\t\tif (value.partitionKey !== this.partitionKey) {\n\t\t\t\tthis.logger.sendTelemetryEvent({\n\t\t\t\t\teventName: FluidCacheGenericEvent.FluidCachePartitionKeyMismatch,\n\t\t\t\t\tsubCategory: FluidCacheEventSubCategories.FluidCache,\n\t\t\t\t\tpkgVersion,\n\t\t\t\t});\n\n\t\t\t\tthis.closeDb(db);\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tconst currentTime = new Date().getTime();\n\n\t\t\t// If too much time has passed since this cache entry was used, we will also return undefined\n\t\t\tif (currentTime - value.createdTimeMs > this.maxCacheItemAge) {\n\t\t\t\tthis.closeDb(db);\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tthis.closeDb(db);\n\t\t\treturn { ...value, dbOpenPerf };\n\t\t} catch (error: any) {\n\t\t\t// We can fail to open the db for a variety of reasons,\n\t\t\t// such as the database version having upgraded underneath us. Return undefined in this case\n\t\t\tthis.logger.sendErrorEvent(\n\t\t\t\t{ eventName: FluidCacheErrorEvent.FluidCacheGetError, pkgVersion },\n\t\t\t\terror,\n\t\t\t);\n\t\t\tthis.closeDb(db);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tpublic async put(entry: ICacheEntry, value: any): Promise<void> {\n\t\tlet db: IDBPDatabase<FluidCacheDBSchema> | undefined;\n\t\ttry {\n\t\t\tdb = await this.openDb();\n\n\t\t\tconst currentTime = new Date().getTime();\n\n\t\t\tawait db.put(\n\t\t\t\tFluidDriverObjectStoreName,\n\t\t\t\t{\n\t\t\t\t\tcachedObject: value,\n\t\t\t\t\tfileId: entry.file.docId,\n\t\t\t\t\ttype: entry.type,\n\t\t\t\t\tcacheItemId: entry.key,\n\t\t\t\t\tpartitionKey: this.partitionKey,\n\t\t\t\t\tcreatedTimeMs: currentTime,\n\t\t\t\t\tlastAccessTimeMs: currentTime,\n\t\t\t\t},\n\t\t\t\tgetKeyForCacheEntry(entry),\n\t\t\t);\n\t\t\tthis.closeDb(db);\n\t\t} catch (error: any) {\n\t\t\t// We can fail to open the db for a variety of reasons,\n\t\t\t// such as the database version having upgraded underneath us\n\t\t\tthis.logger.sendErrorEvent(\n\t\t\t\t{ eventName: FluidCacheErrorEvent.FluidCachePutError, pkgVersion },\n\t\t\t\terror,\n\t\t\t);\n\t\t} finally {\n\t\t\tthis.closeDb(db);\n\t\t}\n\t}\n}\n"]}
|
package/dist/FluidCache.d.ts
CHANGED
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { IPersistedCache, ICacheEntry, IFileEntry } from "@fluidframework/odsp-driver-definitions";
|
|
6
|
-
import { ITelemetryBaseLogger } from "@fluidframework/
|
|
6
|
+
import { ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
|
|
7
|
+
/**
|
|
8
|
+
* @alpha
|
|
9
|
+
*/
|
|
7
10
|
export interface FluidCacheConfig {
|
|
8
11
|
/**
|
|
9
12
|
* A string to specify what partition of the cache you wish to use (e.g. a user id).
|
|
@@ -22,15 +25,28 @@ export interface FluidCacheConfig {
|
|
|
22
25
|
* If an entry exists in the cache, but is older than this value, the cached value will not be returned.
|
|
23
26
|
*/
|
|
24
27
|
maxCacheItemAge: number;
|
|
28
|
+
/**
|
|
29
|
+
* Each time db is opened, it will remain open for this much time. To improve perf, if this property is set as
|
|
30
|
+
* any number greater than 0, then db will not be closed immediately after usage. This value is in milliseconds.
|
|
31
|
+
*/
|
|
32
|
+
closeDbAfterMs?: number;
|
|
25
33
|
}
|
|
26
34
|
/**
|
|
27
|
-
* A cache that can be used by the Fluid ODSP driver to cache data for faster performance
|
|
35
|
+
* A cache that can be used by the Fluid ODSP driver to cache data for faster performance.
|
|
36
|
+
* @alpha
|
|
28
37
|
*/
|
|
29
38
|
export declare class FluidCache implements IPersistedCache {
|
|
30
39
|
private readonly logger;
|
|
31
40
|
private readonly partitionKey;
|
|
32
41
|
private readonly maxCacheItemAge;
|
|
42
|
+
private readonly closeDbImmediately;
|
|
43
|
+
private readonly closeDbAfterMs;
|
|
44
|
+
private db;
|
|
45
|
+
private dbCloseTimer;
|
|
46
|
+
private dbReuseCount;
|
|
33
47
|
constructor(config: FluidCacheConfig);
|
|
48
|
+
private openDb;
|
|
49
|
+
private closeDb;
|
|
34
50
|
removeEntries(file: IFileEntry): Promise<void>;
|
|
35
51
|
get(cacheEntry: ICacheEntry): Promise<any>;
|
|
36
52
|
private getItemFromCache;
|
package/dist/FluidCache.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FluidCache.d.ts","sourceRoot":"","sources":["../src/FluidCache.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"FluidCache.d.ts","sourceRoot":"","sources":["../src/FluidCache.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;AACnG,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAsBvE;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC;;;;;;OAMG;IAEH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5B;;OAEG;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAE9B;;;OAGG;IACH,eAAe,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,qBAAa,UAAW,YAAW,eAAe;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAE7C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAgB;IAE7C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAiB;IACpD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,EAAE,CAA+C;IACzD,OAAO,CAAC,YAAY,CAA4C;IAChE,OAAO,CAAC,YAAY,CAAc;gBAEtB,MAAM,EAAE,gBAAgB;YAiEtB,MAAM;IAwCpB,OAAO,CAAC,OAAO;IAMF,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAyB9C,GAAG,CAAC,UAAU,EAAE,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC;YAoBzC,gBAAgB;IAiDjB,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;CAgC/D"}
|
|
@@ -39,8 +39,8 @@ function getFluidCacheIndexedDbInstance(logger) {
|
|
|
39
39
|
// Catch any error done when attempting to delete the older version.
|
|
40
40
|
// If the object does not exist db will throw.
|
|
41
41
|
// We can now assume that the old version is no longer there regardless.
|
|
42
|
-
telemetry_utils_1.
|
|
43
|
-
eventName: "FluidCacheDeleteOldDbError" /* FluidCacheDeleteOldDbError */,
|
|
42
|
+
(0, telemetry_utils_1.createChildLogger)({ logger }).sendErrorEvent({
|
|
43
|
+
eventName: "FluidCacheDeleteOldDbError" /* FluidCacheErrorEvent.FluidCacheDeleteOldDbError */,
|
|
44
44
|
}, error);
|
|
45
45
|
}
|
|
46
46
|
const cacheObjectStore = db.createObjectStore(exports.FluidDriverObjectStoreName);
|
|
@@ -56,10 +56,14 @@ function getFluidCacheIndexedDbInstance(logger) {
|
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
58
|
exports.getFluidCacheIndexedDbInstance = getFluidCacheIndexedDbInstance;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Deletes the indexed DB instance.
|
|
61
|
+
*
|
|
62
|
+
* @remarks Warning this can throw an error in Firefox incognito, where accessing storage is prohibited.
|
|
63
|
+
* @alpha
|
|
64
|
+
*/
|
|
65
|
+
function deleteFluidCacheIndexDbInstance(deleteDBCallbacks) {
|
|
66
|
+
return (0, idb_1.deleteDB)(exports.FluidDriverCacheDBName, deleteDBCallbacks);
|
|
63
67
|
}
|
|
64
68
|
exports.deleteFluidCacheIndexDbInstance = deleteFluidCacheIndexDbInstance;
|
|
65
|
-
//# sourceMappingURL=FluidCacheIndexedDb.
|
|
69
|
+
//# sourceMappingURL=FluidCacheIndexedDb.cjs.map
|