@atlaspack/cache 3.1.1-canary.41 → 3.1.1-canary.43
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/lib/FSCache.js +21 -0
- package/lib/LMDBLiteCache.js +65 -18
- package/package.json +8 -8
- package/src/FSCache.js +9 -0
- package/src/LMDBLiteCache.js +70 -20
- package/test/LMDBLiteCache.test.js +31 -6
package/lib/FSCache.js
CHANGED
@@ -25,6 +25,20 @@ function _util() {
|
|
25
25
|
};
|
26
26
|
return data;
|
27
27
|
}
|
28
|
+
function _rust() {
|
29
|
+
const data = require("@atlaspack/rust");
|
30
|
+
_rust = function () {
|
31
|
+
return data;
|
32
|
+
};
|
33
|
+
return data;
|
34
|
+
}
|
35
|
+
function _featureFlags() {
|
36
|
+
const data = require("@atlaspack/feature-flags");
|
37
|
+
_featureFlags = function () {
|
38
|
+
return data;
|
39
|
+
};
|
40
|
+
return data;
|
41
|
+
}
|
28
42
|
function _logger() {
|
29
43
|
const data = _interopRequireDefault(require("@atlaspack/logger"));
|
30
44
|
_logger = function () {
|
@@ -62,6 +76,10 @@ class FSCache {
|
|
62
76
|
await Promise.all(dirPromises);
|
63
77
|
}
|
64
78
|
_getCachePath(cacheId) {
|
79
|
+
if ((0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements')) {
|
80
|
+
const cleanId = (0, _rust().hashString)(cacheId);
|
81
|
+
return _path().default.join(this.dir, cleanId.slice(0, 2), cleanId.slice(2));
|
82
|
+
}
|
65
83
|
return _path().default.join(this.dir, cacheId.slice(0, 2), cacheId.slice(2));
|
66
84
|
}
|
67
85
|
getStream(key) {
|
@@ -91,6 +109,9 @@ class FSCache {
|
|
91
109
|
}
|
92
110
|
}
|
93
111
|
#getFilePath(key, index) {
|
112
|
+
if ((0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements')) {
|
113
|
+
return _path().default.join(this.dir, `${(0, _rust().hashString)(key)}-${index}`);
|
114
|
+
}
|
94
115
|
return _path().default.join(this.dir, `${key}-${index}`);
|
95
116
|
}
|
96
117
|
async #unlinkChunks(key, index) {
|
package/lib/LMDBLiteCache.js
CHANGED
@@ -70,6 +70,13 @@ function _fs2() {
|
|
70
70
|
}
|
71
71
|
var _package = _interopRequireDefault(require("../package.json"));
|
72
72
|
var _FSCache = require("./FSCache");
|
73
|
+
function _logger() {
|
74
|
+
const data = require("@atlaspack/logger");
|
75
|
+
_logger = function () {
|
76
|
+
return data;
|
77
|
+
};
|
78
|
+
return data;
|
79
|
+
}
|
73
80
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
74
81
|
// $FlowFixMe
|
75
82
|
const ncpAsync = (0, _util().promisify)(_ncp().default);
|
@@ -121,9 +128,14 @@ function open(directory
|
|
121
128
|
}
|
122
129
|
const pipeline = (0, _util().promisify)(_stream().default.pipeline);
|
123
130
|
class LMDBLiteCache {
|
131
|
+
/**
|
132
|
+
* Directory where we store raw files.
|
133
|
+
*/
|
134
|
+
|
124
135
|
constructor(cacheDir) {
|
125
136
|
this.fs = new (_fs2().NodeFS)();
|
126
137
|
this.dir = cacheDir;
|
138
|
+
this.cacheFilesDirectory = _path().default.join(cacheDir, 'files');
|
127
139
|
this.fsCache = new _FSCache.FSCache(this.fs, cacheDir);
|
128
140
|
this.store = open(cacheDir, {
|
129
141
|
name: 'parcel-cache',
|
@@ -142,6 +154,7 @@ class LMDBLiteCache {
|
|
142
154
|
if (!(0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements')) {
|
143
155
|
await this.fsCache.ensure();
|
144
156
|
}
|
157
|
+
await this.fs.mkdirp(this.cacheFilesDirectory);
|
145
158
|
return Promise.resolve();
|
146
159
|
}
|
147
160
|
serialize() {
|
@@ -166,10 +179,20 @@ class LMDBLiteCache {
|
|
166
179
|
await this.setBlob(key, (0, _buildCache().serialize)(value));
|
167
180
|
}
|
168
181
|
getStream(key) {
|
169
|
-
|
182
|
+
if (!(0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements')) {
|
183
|
+
return this.fs.createReadStream(_path().default.join(this.dir, key));
|
184
|
+
}
|
185
|
+
return _fs().default.createReadStream(this.getFileKey(key));
|
170
186
|
}
|
171
|
-
setStream(key, stream) {
|
172
|
-
|
187
|
+
async setStream(key, stream) {
|
188
|
+
if (!(0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements')) {
|
189
|
+
return pipeline(stream, this.fs.createWriteStream(_path().default.join(this.dir, key)));
|
190
|
+
}
|
191
|
+
const filePath = this.getFileKey(key);
|
192
|
+
await _fs().default.promises.mkdir(_path().default.dirname(filePath), {
|
193
|
+
recursive: true
|
194
|
+
});
|
195
|
+
return pipeline(stream, _fs().default.createWriteStream(filePath));
|
173
196
|
}
|
174
197
|
|
175
198
|
// eslint-disable-next-line require-await
|
@@ -189,34 +212,27 @@ class LMDBLiteCache {
|
|
189
212
|
getBuffer(key) {
|
190
213
|
return Promise.resolve(this.store.get(key));
|
191
214
|
}
|
192
|
-
#getFilePath(key, index) {
|
193
|
-
return _path().default.join(this.dir, `${key}-${index}`);
|
194
|
-
}
|
195
215
|
hasLargeBlob(key) {
|
196
216
|
if (!(0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements')) {
|
197
217
|
return this.fsCache.hasLargeBlob(key);
|
198
218
|
}
|
199
|
-
return this.
|
219
|
+
return _fs().default.promises.access(this.getFileKey(key), _fs().default.constants.F_OK).then(() => true).catch(() => false);
|
200
220
|
}
|
201
|
-
|
202
|
-
/**
|
203
|
-
* @deprecated Use getBlob instead.
|
204
|
-
*/
|
205
221
|
getLargeBlob(key) {
|
206
222
|
if (!(0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements')) {
|
207
223
|
return this.fsCache.getLargeBlob(key);
|
208
224
|
}
|
209
|
-
return
|
225
|
+
return _fs().default.promises.readFile(this.getFileKey(key));
|
210
226
|
}
|
211
|
-
|
212
|
-
/**
|
213
|
-
* @deprecated Use setBlob instead.
|
214
|
-
*/
|
215
|
-
setLargeBlob(key, contents, options) {
|
227
|
+
async setLargeBlob(key, contents, options) {
|
216
228
|
if (!(0, _featureFlags().getFeatureFlag)('cachePerformanceImprovements')) {
|
217
229
|
return this.fsCache.setLargeBlob(key, contents, options);
|
218
230
|
}
|
219
|
-
|
231
|
+
const targetPath = this.getFileKey(key);
|
232
|
+
await _fs().default.promises.mkdir(_path().default.dirname(targetPath), {
|
233
|
+
recursive: true
|
234
|
+
});
|
235
|
+
return _fs().default.promises.writeFile(targetPath, contents);
|
220
236
|
}
|
221
237
|
|
222
238
|
/**
|
@@ -247,6 +263,37 @@ class LMDBLiteCache {
|
|
247
263
|
this.store.compact(_path().default.join(targetPath, 'data.mdb'));
|
248
264
|
}
|
249
265
|
refresh() {}
|
266
|
+
|
267
|
+
/**
|
268
|
+
* Streams, packages are stored in files instead of LMDB.
|
269
|
+
*
|
270
|
+
* On this case, if a cache key happens to have a parent traversal, ../..
|
271
|
+
* it is treated specially
|
272
|
+
*
|
273
|
+
* That is, something/../something and something are meant to be different
|
274
|
+
* keys.
|
275
|
+
*
|
276
|
+
* Plus we do not want to store values outside of the cache directory.
|
277
|
+
*/
|
278
|
+
getFileKey(key) {
|
279
|
+
const cleanKey = key.split('/').map(part => {
|
280
|
+
if (part === '..') {
|
281
|
+
return '$$__parent_dir$$';
|
282
|
+
}
|
283
|
+
return part;
|
284
|
+
}).join('/');
|
285
|
+
return _path().default.join(this.cacheFilesDirectory, cleanKey);
|
286
|
+
}
|
287
|
+
async clear() {
|
288
|
+
await (0, _logger().instrumentAsync)('LMDBLiteCache::clear', async () => {
|
289
|
+
const keys = await this.keys();
|
290
|
+
for (const key of keys) {
|
291
|
+
await this.store.delete(key);
|
292
|
+
}
|
293
|
+
await this.fs.rimraf(this.cacheFilesDirectory);
|
294
|
+
await this.fs.mkdirp(this.cacheFilesDirectory);
|
295
|
+
});
|
296
|
+
}
|
250
297
|
}
|
251
298
|
exports.LMDBLiteCache = LMDBLiteCache;
|
252
299
|
(0, _buildCache().registerSerializableClass)(`${_package.default.version}:LMDBLiteCache`, LMDBLiteCache);
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@atlaspack/cache",
|
3
3
|
"description": "Interface for defining caches and file-system, IDB and LMDB implementations.",
|
4
|
-
"version": "3.1.1-canary.
|
4
|
+
"version": "3.1.1-canary.43+0e0458eed",
|
5
5
|
"license": "(MIT OR Apache-2.0)",
|
6
6
|
"type": "commonjs",
|
7
7
|
"publishConfig": {
|
@@ -23,12 +23,12 @@
|
|
23
23
|
"check-ts": "tsc --noEmit index.d.ts"
|
24
24
|
},
|
25
25
|
"dependencies": {
|
26
|
-
"@atlaspack/build-cache": "2.13.3-canary.
|
27
|
-
"@atlaspack/feature-flags": "2.14.1-canary.
|
28
|
-
"@atlaspack/fs": "2.14.5-canary.
|
29
|
-
"@atlaspack/logger": "2.14.5-canary.
|
30
|
-
"@atlaspack/rust": "3.2.1-canary.
|
31
|
-
"@atlaspack/utils": "2.14.5-canary.
|
26
|
+
"@atlaspack/build-cache": "2.13.3-canary.111+0e0458eed",
|
27
|
+
"@atlaspack/feature-flags": "2.14.1-canary.111+0e0458eed",
|
28
|
+
"@atlaspack/fs": "2.14.5-canary.43+0e0458eed",
|
29
|
+
"@atlaspack/logger": "2.14.5-canary.43+0e0458eed",
|
30
|
+
"@atlaspack/rust": "3.2.1-canary.43+0e0458eed",
|
31
|
+
"@atlaspack/utils": "2.14.5-canary.43+0e0458eed",
|
32
32
|
"ncp": "^2.0.0"
|
33
33
|
},
|
34
34
|
"devDependencies": {
|
@@ -37,5 +37,5 @@
|
|
37
37
|
"browser": {
|
38
38
|
"./src/IDBCache.js": "./src/IDBCache.browser.js"
|
39
39
|
},
|
40
|
-
"gitHead": "
|
40
|
+
"gitHead": "0e0458eedab6e398b375b073439f27f8bcd0b490"
|
41
41
|
}
|
package/src/FSCache.js
CHANGED
@@ -9,6 +9,8 @@ import stream from 'stream';
|
|
9
9
|
import path from 'path';
|
10
10
|
import {promisify} from 'util';
|
11
11
|
|
12
|
+
import {hashString} from '@atlaspack/rust';
|
13
|
+
import {getFeatureFlag} from '@atlaspack/feature-flags';
|
12
14
|
import logger from '@atlaspack/logger';
|
13
15
|
import {
|
14
16
|
deserialize,
|
@@ -51,6 +53,10 @@ export class FSCache implements Cache {
|
|
51
53
|
}
|
52
54
|
|
53
55
|
_getCachePath(cacheId: string): FilePath {
|
56
|
+
if (getFeatureFlag('cachePerformanceImprovements')) {
|
57
|
+
const cleanId = hashString(cacheId);
|
58
|
+
return path.join(this.dir, cleanId.slice(0, 2), cleanId.slice(2));
|
59
|
+
}
|
54
60
|
return path.join(this.dir, cacheId.slice(0, 2), cacheId.slice(2));
|
55
61
|
}
|
56
62
|
|
@@ -90,6 +96,9 @@ export class FSCache implements Cache {
|
|
90
96
|
}
|
91
97
|
|
92
98
|
#getFilePath(key: string, index: number): string {
|
99
|
+
if (getFeatureFlag('cachePerformanceImprovements')) {
|
100
|
+
return path.join(this.dir, `${hashString(key)}-${index}`);
|
101
|
+
}
|
93
102
|
return path.join(this.dir, `${key}-${index}`);
|
94
103
|
}
|
95
104
|
|
package/src/LMDBLiteCache.js
CHANGED
@@ -19,6 +19,7 @@ import {NodeFS} from '@atlaspack/fs';
|
|
19
19
|
// $FlowFixMe
|
20
20
|
import packageJson from '../package.json';
|
21
21
|
import {FSCache} from './FSCache';
|
22
|
+
import {instrumentAsync} from '@atlaspack/logger';
|
22
23
|
|
23
24
|
const ncpAsync = promisify(ncp);
|
24
25
|
|
@@ -107,10 +108,15 @@ export class LMDBLiteCache implements Cache {
|
|
107
108
|
dir: FilePath;
|
108
109
|
store: LmdbWrapper;
|
109
110
|
fsCache: FSCache;
|
111
|
+
/**
|
112
|
+
* Directory where we store raw files.
|
113
|
+
*/
|
114
|
+
cacheFilesDirectory: FilePath;
|
110
115
|
|
111
116
|
constructor(cacheDir: FilePath) {
|
112
117
|
this.fs = new NodeFS();
|
113
118
|
this.dir = cacheDir;
|
119
|
+
this.cacheFilesDirectory = path.join(cacheDir, 'files');
|
114
120
|
this.fsCache = new FSCache(this.fs, cacheDir);
|
115
121
|
|
116
122
|
this.store = open(cacheDir, {
|
@@ -131,6 +137,7 @@ export class LMDBLiteCache implements Cache {
|
|
131
137
|
if (!getFeatureFlag('cachePerformanceImprovements')) {
|
132
138
|
await this.fsCache.ensure();
|
133
139
|
}
|
140
|
+
await this.fs.mkdirp(this.cacheFilesDirectory);
|
134
141
|
return Promise.resolve();
|
135
142
|
}
|
136
143
|
|
@@ -162,14 +169,24 @@ export class LMDBLiteCache implements Cache {
|
|
162
169
|
}
|
163
170
|
|
164
171
|
getStream(key: string): Readable {
|
165
|
-
|
172
|
+
if (!getFeatureFlag('cachePerformanceImprovements')) {
|
173
|
+
return this.fs.createReadStream(path.join(this.dir, key));
|
174
|
+
}
|
175
|
+
|
176
|
+
return fs.createReadStream(this.getFileKey(key));
|
166
177
|
}
|
167
178
|
|
168
|
-
setStream(key: string, stream: Readable): Promise<void> {
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
179
|
+
async setStream(key: string, stream: Readable): Promise<void> {
|
180
|
+
if (!getFeatureFlag('cachePerformanceImprovements')) {
|
181
|
+
return pipeline(
|
182
|
+
stream,
|
183
|
+
this.fs.createWriteStream(path.join(this.dir, key)),
|
184
|
+
);
|
185
|
+
}
|
186
|
+
|
187
|
+
const filePath = this.getFileKey(key);
|
188
|
+
await fs.promises.mkdir(path.dirname(filePath), {recursive: true});
|
189
|
+
return pipeline(stream, fs.createWriteStream(filePath));
|
173
190
|
}
|
174
191
|
|
175
192
|
// eslint-disable-next-line require-await
|
@@ -193,31 +210,25 @@ export class LMDBLiteCache implements Cache {
|
|
193
210
|
return Promise.resolve(this.store.get(key));
|
194
211
|
}
|
195
212
|
|
196
|
-
#getFilePath(key: string, index: number): string {
|
197
|
-
return path.join(this.dir, `${key}-${index}`);
|
198
|
-
}
|
199
|
-
|
200
213
|
hasLargeBlob(key: string): Promise<boolean> {
|
201
214
|
if (!getFeatureFlag('cachePerformanceImprovements')) {
|
202
215
|
return this.fsCache.hasLargeBlob(key);
|
203
216
|
}
|
204
|
-
|
217
|
+
|
218
|
+
return fs.promises
|
219
|
+
.access(this.getFileKey(key), fs.constants.F_OK)
|
220
|
+
.then(() => true)
|
221
|
+
.catch(() => false);
|
205
222
|
}
|
206
223
|
|
207
|
-
/**
|
208
|
-
* @deprecated Use getBlob instead.
|
209
|
-
*/
|
210
224
|
getLargeBlob(key: string): Promise<Buffer> {
|
211
225
|
if (!getFeatureFlag('cachePerformanceImprovements')) {
|
212
226
|
return this.fsCache.getLargeBlob(key);
|
213
227
|
}
|
214
|
-
return
|
228
|
+
return fs.promises.readFile(this.getFileKey(key));
|
215
229
|
}
|
216
230
|
|
217
|
-
|
218
|
-
* @deprecated Use setBlob instead.
|
219
|
-
*/
|
220
|
-
setLargeBlob(
|
231
|
+
async setLargeBlob(
|
221
232
|
key: string,
|
222
233
|
contents: Buffer | string,
|
223
234
|
options?: {|signal?: AbortSignal|},
|
@@ -225,7 +236,10 @@ export class LMDBLiteCache implements Cache {
|
|
225
236
|
if (!getFeatureFlag('cachePerformanceImprovements')) {
|
226
237
|
return this.fsCache.setLargeBlob(key, contents, options);
|
227
238
|
}
|
228
|
-
|
239
|
+
|
240
|
+
const targetPath = this.getFileKey(key);
|
241
|
+
await fs.promises.mkdir(path.dirname(targetPath), {recursive: true});
|
242
|
+
return fs.promises.writeFile(targetPath, contents);
|
229
243
|
}
|
230
244
|
|
231
245
|
/**
|
@@ -262,6 +276,42 @@ export class LMDBLiteCache implements Cache {
|
|
262
276
|
}
|
263
277
|
|
264
278
|
refresh(): void {}
|
279
|
+
|
280
|
+
/**
|
281
|
+
* Streams, packages are stored in files instead of LMDB.
|
282
|
+
*
|
283
|
+
* On this case, if a cache key happens to have a parent traversal, ../..
|
284
|
+
* it is treated specially
|
285
|
+
*
|
286
|
+
* That is, something/../something and something are meant to be different
|
287
|
+
* keys.
|
288
|
+
*
|
289
|
+
* Plus we do not want to store values outside of the cache directory.
|
290
|
+
*/
|
291
|
+
getFileKey(key: string): string {
|
292
|
+
const cleanKey = key
|
293
|
+
.split('/')
|
294
|
+
.map((part) => {
|
295
|
+
if (part === '..') {
|
296
|
+
return '$$__parent_dir$$';
|
297
|
+
}
|
298
|
+
return part;
|
299
|
+
})
|
300
|
+
.join('/');
|
301
|
+
return path.join(this.cacheFilesDirectory, cleanKey);
|
302
|
+
}
|
303
|
+
|
304
|
+
async clear(): Promise<void> {
|
305
|
+
await instrumentAsync('LMDBLiteCache::clear', async () => {
|
306
|
+
const keys = await this.keys();
|
307
|
+
for (const key of keys) {
|
308
|
+
await this.store.delete(key);
|
309
|
+
}
|
310
|
+
|
311
|
+
await this.fs.rimraf(this.cacheFilesDirectory);
|
312
|
+
await this.fs.mkdirp(this.cacheFilesDirectory);
|
313
|
+
});
|
314
|
+
}
|
265
315
|
}
|
266
316
|
|
267
317
|
registerSerializableClass(
|
@@ -16,10 +16,6 @@ describe('LMDBLiteCache', () => {
|
|
16
16
|
await fs.promises.rm(cacheDir, {recursive: true, force: true});
|
17
17
|
});
|
18
18
|
|
19
|
-
afterEach(() => {
|
20
|
-
cache.getNativeRef().close();
|
21
|
-
});
|
22
|
-
|
23
19
|
it('can be constructed', async () => {
|
24
20
|
cache = new LMDBLiteCache(cacheDir);
|
25
21
|
await cache.ensure();
|
@@ -59,11 +55,40 @@ describe('LMDBLiteCache', () => {
|
|
59
55
|
await cache.setBlob('key2', Buffer.from(serialize({value: 43})));
|
60
56
|
await cache.compact(path.join(cacheDir, 'compact_test_compacted'));
|
61
57
|
|
62
|
-
cache.getNativeRef().close();
|
63
|
-
|
64
58
|
cache = new LMDBLiteCache(path.join(cacheDir, 'compact_test_compacted'));
|
65
59
|
await cache.ensure();
|
66
60
|
const keys = cache.keys();
|
67
61
|
assert.deepEqual(Array.from(keys), ['key1', 'key2']);
|
68
62
|
});
|
63
|
+
|
64
|
+
describe('getFileKey', () => {
|
65
|
+
it('should return the correct key', () => {
|
66
|
+
const target = path.join(cacheDir, 'test-file-keys');
|
67
|
+
const cache = new LMDBLiteCache(target);
|
68
|
+
const key = cache.getFileKey('key');
|
69
|
+
assert.equal(key, path.join(target, 'files', 'key'));
|
70
|
+
});
|
71
|
+
|
72
|
+
it('should return the correct key for a key with a parent traversal', () => {
|
73
|
+
const target = path.join(cacheDir, 'test-parent-keys');
|
74
|
+
cache = new LMDBLiteCache(target);
|
75
|
+
const key = cache.getFileKey('../../key');
|
76
|
+
assert.equal(
|
77
|
+
key,
|
78
|
+
path.join(target, 'files', '$$__parent_dir$$/$$__parent_dir$$/key'),
|
79
|
+
);
|
80
|
+
});
|
81
|
+
});
|
82
|
+
|
83
|
+
it('can be closed and re-opened', async () => {
|
84
|
+
cache = new LMDBLiteCache(path.join(cacheDir, 'close_and_reopen_test'));
|
85
|
+
await cache.ensure();
|
86
|
+
await cache.setBlob('key', Buffer.from(serialize({value: 42})));
|
87
|
+
cache.getNativeRef().close();
|
88
|
+
cache = new LMDBLiteCache(path.join(cacheDir, 'close_and_reopen_test'));
|
89
|
+
await cache.ensure();
|
90
|
+
const buffer = await cache.getBlob('key');
|
91
|
+
const result = deserialize(buffer);
|
92
|
+
assert.equal(result.value, 42);
|
93
|
+
});
|
69
94
|
});
|