datafolder 1.0.10 → 1.0.11
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/{internal.md → Internal.md} +8 -1
- package/README.md +3 -1
- package/SKILL.md +191 -0
- package/SSD.md +3 -3
- package/package.json +2 -2
- package/src/QyAclWriter.d.ts +1 -1
- package/src/QyBinWriter.d.ts +1 -1
- package/src/QyKVData.d.ts +1 -1
- package/src/QyKVData.js +42 -42
- package/src/QyKVDataCleaner_Worker.js +37 -37
- package/src/QyLogger.d.ts +1 -0
- package/src/QyLogger.js +1 -0
- package/src/QyMessager_Run.js +4 -3
- package/src/QySnapshotCompactor_Worker.d.ts +1 -1
- package/src/QySnapshotCompactor_Worker.js +4 -4
- package/src/QySnapshotSaver_Worker.js +3 -3
- package/src/QySnapshots.d.ts +2 -2
- package/src/QySnapshots.js +38 -38
- package/src/simulate2.js +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The best mechanism to index and partition data is **by the nature of data**. Instead of forcing data to transform, it should seamlessly embrace and fully cater to the **uniqueness** of each and every data to accomplish nature-level efficiency, **mentally and mechanically**. Whatever data is, it follows like fluid.
|
|
4
4
|
|
|
5
|
-
This is the **design philosophy** behind datafolder, a
|
|
5
|
+
This is the **design philosophy** behind datafolder, a lightning fluid schemaless JSON database, featuring an AI-friendly API for simple and human maintainable code generation, millisecond data response, with full index and trigger features supported, and the unique ability to browse and manage data as a folder tree directly in your browser. Data can be organized naturally into folders, sub folders and files. It works perfectly for a wide range of scenarios, from game/application backends to embedded systems to IoT to edge computing.
|
|
6
6
|
|
|
7
7
|
### User Level Data Layout
|
|
8
8
|
|
|
@@ -24,6 +24,13 @@ Airports can be stored as
|
|
|
24
24
|
airports/{country}/{state}/{airport_id}
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
AI training data can be stored as
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
AITraining/{year}/{month}/{day}/chat_id/prompt
|
|
31
|
+
/answer
|
|
32
|
+
```
|
|
33
|
+
|
|
27
34
|
When you conceive the data model, you don't need to think about tables, schemas, indexes etc. All you need to know is the nature of the data and then go with the most comfortable one for your mental mind.
|
|
28
35
|
|
|
29
36
|
### In-memory Tree Structure
|
package/README.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# datafolder
|
|
2
2
|
|
|
3
|
-
datafolder is a lightning fluid JSON database for
|
|
3
|
+
datafolder is a lightning fluid JSON database for high speed SSDs, featuring an AI-friendly API for simple and human maintainable code generation, millisecond data response, with full index and trigger features supported, and the unique ability to browse and manage data as a folder tree directly in your browser. It works perfectly for a wide range of scenarios, from game/application backends to embedded systems to IoT to edge computing.
|
|
4
4
|
|
|
5
5
|
### Changelog
|
|
6
6
|
|
|
7
|
+
1.0.11 Added [SKILL.md](https://github.com/datafolderdev/datafolder/blob/main/SKILL.md) for feeding to AI
|
|
8
|
+
|
|
7
9
|
1.0.10 Added document [datafolder for high speed SSDs](https://github.com/datafolderdev/datafolder/wiki/datafolder-for-high-speed-SSDs)
|
|
8
10
|
|
|
9
11
|
1.0.9 Added Typescript type declarations.
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: datafolder-builder
|
|
3
|
+
description: >
|
|
4
|
+
Use this skill when building applications on top of datafolder — a lightning fluid JSON database for high speed SSDs, featuring an AI-friendly API for simple and human maintainable code generation, millisecond data response, with full index and trigger features supported, and the unique ability to browse and manage data as a folder tree directly in your browser. Data can be organized naturally into folders, sub folders and files. It works perfectly for a wide range of scenarios, from game/application backends to embedded systems to IoT to edge computing. Covers installation, basic usages, CRUD, batch APIs, powerful query support which auto creates needed indexes, flexible trigger support to monitor any changes.
|
|
5
|
+
Trigger when a user asks to build, query, or operate with datafolder, write application code against it, or understand how to structure data and queries.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# datafolder Builder Skill
|
|
9
|
+
|
|
10
|
+
## What Is datafolder?
|
|
11
|
+
|
|
12
|
+
The best mechanism to index and partition data is **by the nature of data**. Instead of forcing data to transform, it should seamlessly embrace and fully cater to the **uniqueness** of each and every data to accomplish nature-level efficiency, **mentally and mechanically**. Whatever data is, it follows like fluid.
|
|
13
|
+
|
|
14
|
+
This is the **design philosophy** behind datafolder, a lightning fluid schemaless JSON database, featuring an AI-friendly API for simple and human maintainable code generation, millisecond data response, with full index and trigger features supported, and the unique ability to browse and manage data as a folder tree directly in your browser. Data can be organized naturally into folders, sub folders and files. It works perfectly for a wide range of scenarios, from game/application backends to embedded systems to IoT to edge computing.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
🤖 AI-Friendly, Simple API: Designed for easy code generation by AI and long-term maintainability by humans.
|
|
19
|
+
|
|
20
|
+
⚡ Extreme Data Access Speed: Supports fully in-memory operation for millisecond response, with configurable persistence to high-speed SSD.
|
|
21
|
+
|
|
22
|
+
🌐 Built-in Browser Management: Visually browse and manage your data as a folder hierarchy directly within the browser, no separate tools needed.
|
|
23
|
+
|
|
24
|
+
🧩 Plain JavaScript: Built with plain JavaScript, offers full TypeScript support and safe atomic writes.
|
|
25
|
+
|
|
26
|
+
🔧 Designed for application/game backends, IoT, and edge computing scenarios, easily deployable on servers, in browsers, and even on embedded hardware.
|
|
27
|
+
|
|
28
|
+
## User Level Data Layout
|
|
29
|
+
|
|
30
|
+
Data can be organized naturally into folders, sub folders and files. For example, Orders can be stored in
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
orders/{year}/{month}/{day}/{order_id}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Flights can be stored as
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
flights/{from}/{to}/{flight_id}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Airports can be stored as
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
airports/{country}/{state}/{airport_id}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
AI training data can be stored as
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
AITraining/{year}/{month}/{day}/chat_id/prompt
|
|
52
|
+
/answer
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
When you conceive the data model, you don't need to think about tables, schemas, indexes etc. All you need to know is the nature of the data and then go with the most comfortable one for your mental mind.
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
npm install "datafolder"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## How to use
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
import DataFolder from "datafolder";
|
|
67
|
+
const dataFolder = new DataFolder(); //Specify where to store the data. Default is ./data/sampleDataFolder
|
|
68
|
+
await dataFolder.start(); //start() and stop() are the only two async places. All other code doesn't need await.
|
|
69
|
+
dataFolder.insert("traindata/2026/1/20/chat_0001", {
|
|
70
|
+
userid: "user_0001",
|
|
71
|
+
email: "a@b.com",
|
|
72
|
+
feedbackScore: 5,
|
|
73
|
+
address: { city: "a", zipcode: 123 },
|
|
74
|
+
});
|
|
75
|
+
dataFolder.insert("traindata/2026/1/20/chat_0001/prompt", "What is async/await?");
|
|
76
|
+
dataFolder.insert("traindata/2026/1/20/chat_0001/answer", "async/await is ...");
|
|
77
|
+
const { email, password, address, feedbackScore } = dataFolder.view("traindata/2026/1/20/chat_0001");
|
|
78
|
+
address.zipcode = 678;
|
|
79
|
+
dataFolder.insert("traindata/2026/1/20/chat_0001", { feedbackScore: feedbackScore + 1, address });
|
|
80
|
+
await dataFolder.stop();
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Or operate with dir/file objects:
|
|
84
|
+
|
|
85
|
+
```javascript
|
|
86
|
+
const file = dataFolder.file("traindata/2026/1/20/chat_0001");
|
|
87
|
+
const { feedbackScore, address } = file.view({ feedbackScore: 1, address: 1 });
|
|
88
|
+
dataFolder.insert(file, { feedbackScore: feedbackScore + 1, address: { city: "c", zipcode: 663 } });
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Simple CRUD APIs
|
|
92
|
+
|
|
93
|
+
#### Create
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
dataFolder.createFile("path/to/file");
|
|
97
|
+
dataFolder.createDir("path/to/dir");
|
|
98
|
+
dataFolder.insert("path/to/file", { email, address: { city, zipcode } });
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### Read
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
const dir = dataFolder.dir("path/to/dir");
|
|
105
|
+
const { subdirList, fileList, sortedSubdirList } = dir;
|
|
106
|
+
const file = dataFolder.file("path/to/file");
|
|
107
|
+
const { email, address } = dataFolder.view("path/to/file", { email: 1, addres: 1 });
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
#### Update
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
dataFolder.insert("path/to/file", { email: newEmail, address: { zipcode: newZipcode } });
|
|
114
|
+
dataFolder.insert(file, { feedbackScore: feedbackScore + 1 });
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### Delete
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
dataFolder.delDir("path/to/dir");
|
|
121
|
+
dataFolder.delFile("path/to/file");
|
|
122
|
+
dataFolder.delContent("path/to/file");
|
|
123
|
+
dataFolder.remove("path/to/file", { email: 1, address: { zipcode: 1 } });
|
|
124
|
+
dataFolder.remove(file, { email: 1, address: { zipcode: 1 } });
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Batch APIs
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
const [dir, file, { email, feedbackScore }] = dataFolder.fetch
|
|
131
|
+
.dir("path/to/dir")
|
|
132
|
+
.file("path/to/file")
|
|
133
|
+
.view("path/to/file")
|
|
134
|
+
.run();
|
|
135
|
+
dataFolder.batch
|
|
136
|
+
.insert("path/to/chat_details", { user: "user_0001", feedbackScore: 100 })
|
|
137
|
+
.insert("path/to/prompt", "what is LLM?")
|
|
138
|
+
.insert("path/to/answer", "LLM is AI...")
|
|
139
|
+
.remove("path/to/file", { address: { city: 1 } })
|
|
140
|
+
.run();
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Powerful Query Support. Auto Create Needed Indexes
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
// get all the training data in year 2026 with feedbackScore between 60 and 85.
|
|
147
|
+
dataFolder.queryFiles("traindata/2026/**", { feedbackScore: (x) => x >= 60 && x <= 85 });
|
|
148
|
+
|
|
149
|
+
// get training data of user_001 from July to Oct in year 2025, 2026 with feedbackScore higher than 90.
|
|
150
|
+
dataFolder.queryFiles(["traindata", { $in: [2025, 2026] }, { $gt: 6, $lte: 10 }], {
|
|
151
|
+
feedbackScore: { $gt: 90 },
|
|
152
|
+
userId: "user_0001",
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
dataFolder.queryFiles("flights/**", { _from: "airports/LAX" });
|
|
156
|
+
dataFolder.queryFiles(["flights", "*", "jan", (x) => x >= 5 && x <= 7], {
|
|
157
|
+
$or: [{ _from: "airports/BIS" }, { _to: "airports/BIS" }],
|
|
158
|
+
});
|
|
159
|
+
dataFolder.queryFiles(["flights", "*", "feb", { $or: [5, 6, 7] }], {
|
|
160
|
+
$or: [{ _from: "airports/BIS" }, { _to: "airports/BIS" }],
|
|
161
|
+
});
|
|
162
|
+
dataFolder.queryFiles(["flights", "*", "mar", { $gte: 5, lte: 7 }], {
|
|
163
|
+
$or: [{ _from: "airports/BIS" }, { _to: "airports/BIS" }],
|
|
164
|
+
});
|
|
165
|
+
dataFolder.queryFilesMulti([
|
|
166
|
+
[["airbnb", "Loft", "Entire home/apt", "Real Bed"], { address: { street: "Porto, Porto, Portugal" } }],
|
|
167
|
+
["airbnb/Loft/**", { minimum_nights: (x) => x >= 60 }],
|
|
168
|
+
]);
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Flexible Trigger Support to Monitor Any Changes
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
function onPromptScoreChanged({ promptId, feedbackScore }) {
|
|
175
|
+
const { oldValue, newValue } = feedbackScore;
|
|
176
|
+
// Logic to handle prompt feedback score change.
|
|
177
|
+
}
|
|
178
|
+
dataFolder.insertTrigger("promptScoreChanged", "traindata/{promptId}", "feedbackScore");
|
|
179
|
+
dataFolder.on("promptScoreChanged", onPromptScoreChanged);
|
|
180
|
+
dataFolder.insert("traindata/prompt_1", { feedbackScore: 80 });
|
|
181
|
+
dataFolder.insert("traindata/prompt_2", { feedbackScore: 63 });
|
|
182
|
+
dataFolder.insert("traindata/prompt_1", { feedbackScore: 32 });
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Will trigger the following calls:
|
|
186
|
+
|
|
187
|
+
```javascript
|
|
188
|
+
onPromptScoreChanged({ promptId: "student1", feedbackScore: { oldValue: undefined, newValue: 80 } });
|
|
189
|
+
onPromptScoreChanged({ promptId: "student2", feedbackScore: { oldValue: undefined, newValue: 63 } });
|
|
190
|
+
onPromptScoreChanged({ promptId: "student1", feedbackScore: { oldValue: 80, newValue: 32 } });
|
|
191
|
+
```
|
package/SSD.md
CHANGED
|
@@ -34,7 +34,7 @@ Key Characteristics and Benefits of LSM trees to run on SSDs
|
|
|
34
34
|
|
|
35
35
|
### datafolder, a lightning fluid JSON database, is a natural fit for high speed SSDs
|
|
36
36
|
|
|
37
|
-
datafolder is [based on LSM trees](https://github.com/datafolderdev/datafolder/wiki/Internals-of-datafolder), but
|
|
37
|
+
datafolder is [based on LSM trees](https://github.com/datafolderdev/datafolder/wiki/Internals-of-datafolder), but taken to a higher level.
|
|
38
38
|
|
|
39
39
|
- Complex JSON data in a single virtual file for a record
|
|
40
40
|
|
|
@@ -42,7 +42,7 @@ The data is just a normal JSON [object](https://github.com/datafolderdev/datafol
|
|
|
42
42
|
|
|
43
43
|
- Changes are stored as deltas in append-only mode.
|
|
44
44
|
|
|
45
|
-
When changes are
|
|
45
|
+
When changes are made to each virtual file, only [the deltas](https://github.com/datafolderdev/datafolder/wiki/Internals-of-datafolder#accumulated-change-list) are stored. And the delta changes from different virtual files/folders are merged and appended to ACL log files.
|
|
46
46
|
|
|
47
47
|
- Never overwrite. Always append or create new file.
|
|
48
48
|
|
|
@@ -57,4 +57,4 @@ datafolder never overwrites. It always appends to an ACL file, or create new [sn
|
|
|
57
57
|
- Snapshot Position Mapping maps a key to its position in the corresponding valueList file.
|
|
58
58
|
- Snapshot Prefix Position Mapping maps a key prefix to its range position in the corresponding Positing Mapping file.
|
|
59
59
|
|
|
60
|
-
### Future: Make datafolder runs directly on SSDs without the FTL
|
|
60
|
+
### Future: Make datafolder runs directly on SSDs without the FTL, or Zoned Namespaces(ZNS) SSDs (formerly Open-Channel SSDs)
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "datafolder",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "lysh",
|
|
6
6
|
"email": "datafolderdev@outlook.com"
|
|
7
7
|
},
|
|
8
|
-
"description": "datafolder is a lightning fluid JSON database for
|
|
8
|
+
"description": "datafolder is a lightning fluid JSON database for high speed SSDs, featuring an AI-friendly API for simple and human maintainable code generation, millisecond data response, with full index and trigger features supported, and the unique ability to browse and manage data as a folder tree directly in your browser. It works perfectly for a wide range of scenarios, from game/application backends to embedded systems to IoT to edge computing.",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
package/src/QyAclWriter.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { QyBinWriter } from "./QyBinWriter.ts";
|
|
2
2
|
export declare class QyAclWriter extends QyBinWriter {
|
|
3
3
|
qyAclCmdGenerator: any;
|
|
4
|
-
constructor(options: any, filePath?:
|
|
4
|
+
constructor(options: any, filePath?: any);
|
|
5
5
|
getBinData(dataQueue: any, changeId: any): any;
|
|
6
6
|
}
|
package/src/QyBinWriter.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export declare class QyBinWriter extends QyOpRunner {
|
|
|
3
3
|
_filePath: any;
|
|
4
4
|
_totalSavedSize: number;
|
|
5
5
|
fileHandle: any;
|
|
6
|
-
constructor(options?: any, filePath?:
|
|
6
|
+
constructor(options?: any, filePath?: any);
|
|
7
7
|
get filePath(): any;
|
|
8
8
|
set filePath(val: any);
|
|
9
9
|
get totalSavedSize(): number;
|
package/src/QyKVData.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export declare class QyKVData {
|
|
|
10
10
|
compactMaxChangeId: number;
|
|
11
11
|
snapshotSavedChangeId: number;
|
|
12
12
|
maxChangeId: number;
|
|
13
|
-
|
|
13
|
+
emptySnapshotNumVerList: any[];
|
|
14
14
|
isStopping: boolean;
|
|
15
15
|
qySnapshots: any;
|
|
16
16
|
aclKeyValueMap: any;
|
package/src/QyKVData.js
CHANGED
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
} from "./QyDefaultOptions.js";
|
|
12
12
|
|
|
13
13
|
import {
|
|
14
|
-
QyKVDataSaver as
|
|
14
|
+
QyKVDataSaver as p
|
|
15
15
|
} from "./QyKVDataSaver.js";
|
|
16
16
|
|
|
17
17
|
import {
|
|
18
|
-
QySnapshotCompactor as
|
|
18
|
+
QySnapshotCompactor as i
|
|
19
19
|
} from "./QySnapshotCompactor.js";
|
|
20
20
|
|
|
21
21
|
import {
|
|
@@ -44,7 +44,7 @@ class a {
|
|
|
44
44
|
compactMaxChangeId = 0;
|
|
45
45
|
snapshotSavedChangeId = 0;
|
|
46
46
|
maxChangeId = 0;
|
|
47
|
-
|
|
47
|
+
emptySnapshotNumVerList = [];
|
|
48
48
|
isStopping = !1;
|
|
49
49
|
qySnapshots;
|
|
50
50
|
aclKeyValueMap;
|
|
@@ -61,8 +61,8 @@ class a {
|
|
|
61
61
|
options: t,
|
|
62
62
|
kvFolder: a,
|
|
63
63
|
snapshotFolder: e,
|
|
64
|
-
qyKVDataSaver: new
|
|
65
|
-
qySnapshotCompactor: new
|
|
64
|
+
qyKVDataSaver: new p(this, a, t),
|
|
65
|
+
qySnapshotCompactor: new i(this, a, t),
|
|
66
66
|
qyKVDataCleaner: new r(this, a, t)
|
|
67
67
|
});
|
|
68
68
|
}
|
|
@@ -70,12 +70,12 @@ class a {
|
|
|
70
70
|
var {
|
|
71
71
|
options: n,
|
|
72
72
|
qyKVDataSaver: s,
|
|
73
|
-
qySnapshotCompactor:
|
|
74
|
-
qyKVDataCleaner:
|
|
73
|
+
qySnapshotCompactor: p,
|
|
74
|
+
qyKVDataCleaner: i
|
|
75
75
|
} = this, {
|
|
76
76
|
maxSnapshotNum: r,
|
|
77
77
|
snapshotMaxChangeId: h
|
|
78
|
-
} = o, s = (s.start(a, r, t, e),
|
|
78
|
+
} = o, s = (s.start(a, r, t, e), p.start(), i.start(), setInterval(() => l(this), n.compactSnapshotsInterval)), r = setInterval(() => (async o => {
|
|
79
79
|
if (!o.unloadingMemory) {
|
|
80
80
|
o.unloadingMemory = !0;
|
|
81
81
|
var {
|
|
@@ -84,9 +84,9 @@ class a {
|
|
|
84
84
|
} = o;
|
|
85
85
|
let a = 0, t = 0;
|
|
86
86
|
if (e) {
|
|
87
|
-
var s,
|
|
87
|
+
var s, p = n.maxFileCountToUnload, n = e.rootDir;
|
|
88
88
|
t = d(o, n), a = t;
|
|
89
|
-
for (s of n.allLoadedSubdirs()) if ((t += d(o, s)) >
|
|
89
|
+
for (s of n.allLoadedSubdirs()) if ((t += d(o, s)) > p && (a += t,
|
|
90
90
|
t = 0, await c(0), o.isStopping)) break;
|
|
91
91
|
}
|
|
92
92
|
a += t, global.gc && global.gc(), o.unloadingMemory = !1;
|
|
@@ -106,11 +106,11 @@ class a {
|
|
|
106
106
|
} = a;
|
|
107
107
|
console.time("bgLoadPosMaps");
|
|
108
108
|
for (t of Object.keys(o.numToSnapshotMap).sort((a, t) => t - a)) {
|
|
109
|
-
if (await async function s(
|
|
110
|
-
if (
|
|
111
|
-
let d =
|
|
109
|
+
if (await async function s(p, i, r = void 0, h = void 0, c = void 0, l = void 0) {
|
|
110
|
+
if (p.isStopping) return void (l && l.close());
|
|
111
|
+
let d = p.qySnapshots;
|
|
112
112
|
let m = d.numToSnapshotMap;
|
|
113
|
-
let S = m[
|
|
113
|
+
let S = m[i];
|
|
114
114
|
if (!S) return void (l && l.close());
|
|
115
115
|
let {
|
|
116
116
|
info: u,
|
|
@@ -119,19 +119,19 @@ class a {
|
|
|
119
119
|
r = r || Object.keys(v).sort().reverse();
|
|
120
120
|
if (0 == r.length) return void (l && l.close());
|
|
121
121
|
if (null == h || h != u.pMVersion) return l && l.close(), c = d.getPMFilePath(S),
|
|
122
|
-
s(
|
|
122
|
+
s(p, i, r, u.pMVersion, c, await f(c));
|
|
123
123
|
for (;0 < r.length; ) {
|
|
124
124
|
let e = y(r), n = v[e];
|
|
125
125
|
if (n) {
|
|
126
126
|
let [ a, t ] = n, o = await C(c, l, a, t);
|
|
127
|
-
if (
|
|
128
|
-
if (h != u.pMVersion) return l.close(), s(
|
|
127
|
+
if (p.isStopping || !m[i]) return void l.close();
|
|
128
|
+
if (h != u.pMVersion) return l.close(), s(p, i, r);
|
|
129
129
|
e in v && d.loadPartialPosMapBuffer(S, e, o);
|
|
130
130
|
}
|
|
131
131
|
r.pop();
|
|
132
132
|
}
|
|
133
133
|
l.close();
|
|
134
|
-
g.info("bgLoadPosMap:" +
|
|
134
|
+
g.info("bgLoadPosMap:" + i);
|
|
135
135
|
}(a, t), a.isStopping) break;
|
|
136
136
|
if (await c(0), a.isStopping) break;
|
|
137
137
|
}
|
|
@@ -185,12 +185,12 @@ class a {
|
|
|
185
185
|
qyKVDataSaver: n
|
|
186
186
|
} = this;
|
|
187
187
|
if (3 == o && e <= a && (L(this), this.compactState = 1), t) {
|
|
188
|
-
var s, o = t,
|
|
188
|
+
var s, o = t, p = this.qySnapshots, i = p.keyToSnapshotMap;
|
|
189
189
|
for (s in o.posMap) {
|
|
190
|
-
var r =
|
|
191
|
-
r &&
|
|
190
|
+
var r = i[s];
|
|
191
|
+
r && p.removeKeyFromSnapshot(r, s);
|
|
192
192
|
}
|
|
193
|
-
|
|
193
|
+
p.addMaxSnapshot(o), g.info("addPMOnlySnapshot: " + p.snapshotMaxChangeId),
|
|
194
194
|
n.releaseSnapshot(t.snapshotNum);
|
|
195
195
|
}
|
|
196
196
|
}
|
|
@@ -205,11 +205,11 @@ class a {
|
|
|
205
205
|
var t = a, o = l.qySnapshots.numToSnapshotMap;
|
|
206
206
|
for (s in t) {
|
|
207
207
|
var e, n = t[s], s = o[s], {
|
|
208
|
-
prefixPM:
|
|
209
|
-
info:
|
|
208
|
+
prefixPM: p,
|
|
209
|
+
info: i
|
|
210
210
|
} = s;
|
|
211
|
-
for (e in ++
|
|
212
|
-
|
|
211
|
+
for (e in ++i.pMVersion, i.verList[0] = i.pMVersion + i.vLVersion,
|
|
212
|
+
p) p[e] = n[e];
|
|
213
213
|
(a => {
|
|
214
214
|
var {
|
|
215
215
|
info: t,
|
|
@@ -243,26 +243,26 @@ class a {
|
|
|
243
243
|
keyToSnapshotMap: s
|
|
244
244
|
} = e, {
|
|
245
245
|
snapshotNum: a,
|
|
246
|
-
|
|
247
|
-
posMap:
|
|
248
|
-
} = d,
|
|
246
|
+
combinedSnapshotNumVerList: t,
|
|
247
|
+
posMap: p
|
|
248
|
+
} = d, i = n[a], {
|
|
249
249
|
posMap: r,
|
|
250
250
|
info: o
|
|
251
|
-
} =
|
|
251
|
+
} = i;
|
|
252
252
|
for (M in ++o.pMVersion, ++o.vLVersion, o.verList[0] = o.pMVersion + o.vLVersion,
|
|
253
|
-
r) r[M] =
|
|
254
|
-
q(
|
|
255
|
-
let h = o.totalCount, c =
|
|
253
|
+
r) r[M] = p[M];
|
|
254
|
+
q(i);
|
|
255
|
+
let h = o.totalCount, c = i.outdatedCount;
|
|
256
256
|
for (let [ o ] of t) {
|
|
257
257
|
var V, I = n[o];
|
|
258
258
|
let {
|
|
259
259
|
posMap: a,
|
|
260
260
|
info: t
|
|
261
261
|
} = I;
|
|
262
|
-
for (V in a) r[V] =
|
|
262
|
+
for (V in a) r[V] = p[V], s[V] = i;
|
|
263
263
|
q(I), h += t.totalCount, c += I.outdatedCount, e.delSnapshot(I);
|
|
264
264
|
}
|
|
265
|
-
o.totalCount = h,
|
|
265
|
+
o.totalCount = h, i.outdatedCount = c;
|
|
266
266
|
}
|
|
267
267
|
this.snapshotSavedChangeId >= this.compactMaxChangeId ? (L(this), this.compactState = 1) : this.compactState = 3;
|
|
268
268
|
}
|
|
@@ -285,20 +285,20 @@ function l(a) {
|
|
|
285
285
|
qySnapshots: o,
|
|
286
286
|
qySnapshotCompactor: e
|
|
287
287
|
} = a, {
|
|
288
|
-
|
|
288
|
+
emptySnapshotNumVerList: o,
|
|
289
289
|
pMCompactSnapshotList: n,
|
|
290
290
|
vLCompactSnapshotList: s,
|
|
291
|
-
combiningSnapshotList:
|
|
291
|
+
combiningSnapshotList: p
|
|
292
292
|
} = o.calOutdates();
|
|
293
|
-
if (0 <
|
|
293
|
+
if (0 < p.length || 0 < n.length || 0 < s.length) {
|
|
294
294
|
Object.assign(a, {
|
|
295
295
|
compactState: 2,
|
|
296
296
|
compactMaxChangeId: t
|
|
297
297
|
});
|
|
298
|
-
for (var
|
|
299
|
-
e.compactSnapshots(n, s,
|
|
298
|
+
for (var i of [ n, s, p ]) for (var r of i) r.compactOutedCount = r.outdatedCount;
|
|
299
|
+
e.compactSnapshots(n, s, p);
|
|
300
300
|
}
|
|
301
|
-
0 < o.length && (a.
|
|
301
|
+
0 < o.length && (a.emptySnapshotNumVerList.push(...o), a.snapshotSavedChangeId >= t ? h(a) : 1 == a.compactState && Object.assign(a, {
|
|
302
302
|
compactState: 3,
|
|
303
303
|
compactMaxChangeId: t
|
|
304
304
|
}));
|
|
@@ -315,7 +315,7 @@ function d(a, t) {
|
|
|
315
315
|
function h(a) {
|
|
316
316
|
var {
|
|
317
317
|
qyKVDataCleaner: a,
|
|
318
|
-
|
|
318
|
+
emptySnapshotNumVerList: t
|
|
319
319
|
} = a;
|
|
320
320
|
0 < t.length && (a.removeSnapshotsFiles(t), t.length = 0);
|
|
321
321
|
}
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from "./QyDefaultOptions.js";
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
|
-
QyMessageWorker as
|
|
16
|
+
QyMessageWorker as e
|
|
17
17
|
} from "./QyMessageWorker.js";
|
|
18
18
|
|
|
19
19
|
import {
|
|
@@ -31,10 +31,10 @@ import {
|
|
|
31
31
|
} from "./QyLogger.js";
|
|
32
32
|
|
|
33
33
|
import {
|
|
34
|
-
|
|
34
|
+
getInfoFileName as l
|
|
35
35
|
} from "./QySnapshots.js";
|
|
36
36
|
|
|
37
|
-
export default class extends
|
|
37
|
+
export default class extends e {
|
|
38
38
|
kvFolder;
|
|
39
39
|
snapshotFolder;
|
|
40
40
|
snapshotInfoFolder;
|
|
@@ -42,82 +42,82 @@ export default class extends s {
|
|
|
42
42
|
historyAclFolder;
|
|
43
43
|
historySnapshotFolder;
|
|
44
44
|
constructor({
|
|
45
|
-
kvFolder:
|
|
46
|
-
options:
|
|
45
|
+
kvFolder: e,
|
|
46
|
+
options: s
|
|
47
47
|
}) {
|
|
48
|
-
super(
|
|
48
|
+
super(s = {
|
|
49
49
|
...a("QyKVDataCleaner_Worker"),
|
|
50
|
-
...
|
|
50
|
+
...s
|
|
51
51
|
}, [], [ "cleanAclFiles", "cleanPMFiles", "cleanVLFiles", "removeSnapshotsFiles" ]);
|
|
52
|
-
var
|
|
52
|
+
var s = m(e, "history"), o = m(e, "snapshot");
|
|
53
53
|
Object.assign(this, {
|
|
54
|
-
kvFolder:
|
|
54
|
+
kvFolder: e,
|
|
55
55
|
snapshotFolder: o,
|
|
56
56
|
snapshotInfoFolder: m(o, "info"),
|
|
57
|
-
aclFolder: m(
|
|
58
|
-
historyAclFolder: m(
|
|
59
|
-
historySnapshotFolder: m(
|
|
57
|
+
aclFolder: m(e, "acl"),
|
|
58
|
+
historyAclFolder: m(s, "acl"),
|
|
59
|
+
historySnapshotFolder: m(s, "snapshot")
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
_op_start() {}
|
|
63
63
|
_op_stop() {}
|
|
64
|
-
async _op_cleanAclFiles(
|
|
65
|
-
var
|
|
66
|
-
if (await d(
|
|
64
|
+
async _op_cleanAclFiles(e) {
|
|
65
|
+
var s = this, o = e, e = s.aclFolder;
|
|
66
|
+
if (await d(e)) {
|
|
67
67
|
var a, r = [];
|
|
68
|
-
for (a of (await h(
|
|
68
|
+
for (a of (await h(e)).filter(e => /^[1-9]/.test(e))) parseInt(a) <= o && r.push((async (e, s) => {
|
|
69
69
|
var {
|
|
70
70
|
aclFolder: o,
|
|
71
71
|
historyAclFolder: a
|
|
72
|
-
} =
|
|
73
|
-
return await F(a), y(
|
|
74
|
-
})(
|
|
72
|
+
} = e;
|
|
73
|
+
return await F(a), y(e, s, o, a);
|
|
74
|
+
})(s, a));
|
|
75
75
|
0 < r.length && await Promise.allSettled(r);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
-
async _op_cleanPMFiles(
|
|
79
|
-
await Promise.allSettled(
|
|
80
|
-
return o = this, a =
|
|
78
|
+
async _op_cleanPMFiles(e) {
|
|
79
|
+
await Promise.allSettled(e.map(([ e, s ]) => {
|
|
80
|
+
return o = this, a = e, r = s, Promise.all([ "pM", "prefixPM" ].map(e => n(o, a, r, e)));
|
|
81
81
|
var o, a, r;
|
|
82
82
|
}));
|
|
83
83
|
}
|
|
84
|
-
async _op_cleanVLFiles(
|
|
85
|
-
await n(this,
|
|
84
|
+
async _op_cleanVLFiles(e, s) {
|
|
85
|
+
await n(this, e, s, "vL");
|
|
86
86
|
}
|
|
87
|
-
async _op_removeSnapshotsFiles(
|
|
87
|
+
async _op_removeSnapshotsFiles(e) {
|
|
88
88
|
let {
|
|
89
89
|
snapshotFolder: o,
|
|
90
90
|
snapshotInfoFolder: a
|
|
91
91
|
} = this;
|
|
92
|
-
await Promise.all(
|
|
93
|
-
await c(m(a, l(
|
|
94
|
-
await c(m(o, "" +
|
|
92
|
+
await Promise.all(e.map(async ([ s, e ]) => {
|
|
93
|
+
await c(m(a, l(s, e))), await Promise.all([ "pM", "prefixPM", "vL" ].map(e => n(this, s, 1 / 0, e))),
|
|
94
|
+
await c(m(o, "" + s), {
|
|
95
95
|
recursive: !0
|
|
96
96
|
});
|
|
97
97
|
}));
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
function y(
|
|
102
|
-
o = m(o,
|
|
103
|
-
return
|
|
101
|
+
function y(e, s, o, a) {
|
|
102
|
+
o = m(o, s), a = m(a, s);
|
|
103
|
+
return e.options.compressRedundantFile ? t(o, a + i, {
|
|
104
104
|
override: !0,
|
|
105
105
|
removeSrcAfterSuccess: !0
|
|
106
106
|
}) : (f.info(`rename ${o} to ` + a), r(o, a));
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
async function n(
|
|
109
|
+
async function n(s, o, a, r) {
|
|
110
110
|
var {
|
|
111
|
-
snapshotFolder:
|
|
111
|
+
snapshotFolder: e,
|
|
112
112
|
historySnapshotFolder: t
|
|
113
|
-
} =
|
|
113
|
+
} = s, i = m(e, "" + o);
|
|
114
114
|
if (await d(i)) {
|
|
115
|
-
let
|
|
115
|
+
let e;
|
|
116
116
|
var l, n = [];
|
|
117
117
|
for (l of await h(i)) {
|
|
118
118
|
var p = new RegExp("^([0-9]+)_" + r).exec(l);
|
|
119
|
-
p && (p = parseInt(p[1])) < a && (1 == p ? (
|
|
120
|
-
await F(
|
|
119
|
+
p && (p = parseInt(p[1])) < a && (1 == p ? (e || (e = m(t, "" + o),
|
|
120
|
+
await F(e)), n.push(y(s, l, i, e))) : (p = m(i, l), f.info("Removing " + p),
|
|
121
121
|
n.push(c(p))));
|
|
122
122
|
}
|
|
123
123
|
0 < n.length && await Promise.allSettled(n);
|
package/src/QyLogger.d.ts
CHANGED
package/src/QyLogger.js
CHANGED
package/src/QyMessager_Run.js
CHANGED
|
@@ -14,11 +14,12 @@ import {
|
|
|
14
14
|
} from "node:worker_threads";
|
|
15
15
|
|
|
16
16
|
import {
|
|
17
|
-
logger as s
|
|
17
|
+
logger as s,
|
|
18
|
+
ThreadMark as n
|
|
18
19
|
} from "./QyLogger.js";
|
|
19
20
|
|
|
20
21
|
import {
|
|
21
|
-
__dirname as
|
|
22
|
+
__dirname as m
|
|
22
23
|
} from "./QyUtils.js";
|
|
23
24
|
|
|
24
25
|
(async () => {
|
|
@@ -27,5 +28,5 @@ import {
|
|
|
27
28
|
workerTypeFileName: e,
|
|
28
29
|
initArgMap: r
|
|
29
30
|
} = i;
|
|
30
|
-
new (await import(t("file://" + o(
|
|
31
|
+
new (await import(t("file://" + o(m, e)))).default(r), s.log("" + n + e);
|
|
31
32
|
})();
|
|
@@ -6,7 +6,7 @@ export default class QySnapshotCompactor_Worker extends QyMessageWorker {
|
|
|
6
6
|
qyKVDataCleaner: any;
|
|
7
7
|
pMInfoMap: any;
|
|
8
8
|
vLInfoMap: any;
|
|
9
|
-
|
|
9
|
+
combinedSnapshotNumVerList: any[];
|
|
10
10
|
constructor({ kvFolder, options }: {
|
|
11
11
|
kvFolder: any;
|
|
12
12
|
options: any;
|
|
@@ -35,7 +35,7 @@ export default class extends a {
|
|
|
35
35
|
qyKVDataCleaner;
|
|
36
36
|
pMInfoMap = {};
|
|
37
37
|
vLInfoMap = {};
|
|
38
|
-
|
|
38
|
+
combinedSnapshotNumVerList = [];
|
|
39
39
|
constructor({
|
|
40
40
|
kvFolder: a,
|
|
41
41
|
options: s
|
|
@@ -97,7 +97,7 @@ export default class extends a {
|
|
|
97
97
|
let {
|
|
98
98
|
qySnapshots: o,
|
|
99
99
|
vLInfoMap: e,
|
|
100
|
-
|
|
100
|
+
combinedSnapshotNumVerList: p
|
|
101
101
|
} = t, n = o.getMaxCombinedSnapshot(a), {
|
|
102
102
|
snapshotNum: i,
|
|
103
103
|
info: r,
|
|
@@ -122,7 +122,7 @@ export default class extends a {
|
|
|
122
122
|
}
|
|
123
123
|
await o.saveKeyValueMap(n), e[i] = r, s.combinationUpdate = {
|
|
124
124
|
snapshotNum: i,
|
|
125
|
-
|
|
125
|
+
combinedSnapshotNumVerList: p,
|
|
126
126
|
posMap: n.posMap
|
|
127
127
|
};
|
|
128
128
|
})(this, s, t)), await Promise.all(e), this.castParent("onCompactUpdates", t);
|
|
@@ -132,7 +132,7 @@ export default class extends a {
|
|
|
132
132
|
qySnapshots: o,
|
|
133
133
|
pMInfoMap: t,
|
|
134
134
|
vLInfoMap: e,
|
|
135
|
-
|
|
135
|
+
combinedSnapshotNumVerList: p,
|
|
136
136
|
qyKVDataCleaner: n
|
|
137
137
|
} = this, i = (Object.assign(this, {
|
|
138
138
|
pMInfoMap: {},
|
|
@@ -87,7 +87,7 @@ export default class extends a {
|
|
|
87
87
|
snapshotNum: a,
|
|
88
88
|
maxChangeId: t
|
|
89
89
|
} = await a, {
|
|
90
|
-
|
|
90
|
+
emptySnapshotNumVerList: s,
|
|
91
91
|
pMChangeList: o,
|
|
92
92
|
vLChange: n,
|
|
93
93
|
snapshotInfoMap: p,
|
|
@@ -120,7 +120,7 @@ export default class extends a {
|
|
|
120
120
|
function h(a, s, e, t) {
|
|
121
121
|
var o = (a => {
|
|
122
122
|
var s, e = a.qySnapshots, {
|
|
123
|
-
|
|
123
|
+
emptySnapshotNumVerList: t,
|
|
124
124
|
combiningSnapshotList: a,
|
|
125
125
|
pMCompactSnapshotList: o
|
|
126
126
|
} = e.calOutdates(), n = {}, p = [], h = [], r = [];
|
|
@@ -155,7 +155,7 @@ function h(a, s, e, t) {
|
|
|
155
155
|
};
|
|
156
156
|
}
|
|
157
157
|
return {
|
|
158
|
-
|
|
158
|
+
emptySnapshotNumVerList: t,
|
|
159
159
|
pMChangeList: h,
|
|
160
160
|
vLChange: r,
|
|
161
161
|
snapshotInfoMap: n,
|
package/src/QySnapshots.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export declare class QySnapshots {
|
|
|
21
21
|
loadPartialPosMapBuffer(snapshot: any, prefix: any, buffer: any): void;
|
|
22
22
|
delSnapshot(snapshot: any): void;
|
|
23
23
|
calOutdates(): {
|
|
24
|
-
|
|
24
|
+
emptySnapshotNumVerList: any[];
|
|
25
25
|
combiningSnapshotList: any[];
|
|
26
26
|
pMCompactSnapshotList: any[];
|
|
27
27
|
vLCompactSnapshotList: any[];
|
|
@@ -36,4 +36,4 @@ export declare class QySnapshots {
|
|
|
36
36
|
}
|
|
37
37
|
export declare function iterPMBuffer(buffer: any, fun: any): void;
|
|
38
38
|
export declare function loadPrefixPMFile(prefixPMFilePath: any): Promise<{}>;
|
|
39
|
-
export declare function
|
|
39
|
+
export declare function getInfoFileName(snapshotNum: any, version: any): string;
|
package/src/QySnapshots.js
CHANGED
|
@@ -32,9 +32,9 @@ import {
|
|
|
32
32
|
} from "./QyBinWriter.js";
|
|
33
33
|
|
|
34
34
|
import {
|
|
35
|
-
bufferToNums as
|
|
36
|
-
numsToBuffer as
|
|
37
|
-
applyAclChange as
|
|
35
|
+
bufferToNums as y,
|
|
36
|
+
numsToBuffer as c,
|
|
37
|
+
applyAclChange as g,
|
|
38
38
|
iterCmdKeys as S
|
|
39
39
|
} from "./QyAcl.js";
|
|
40
40
|
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
logger as P
|
|
47
47
|
} from "./QyLogger.js";
|
|
48
48
|
|
|
49
|
-
let x = Array.isArray, F = Buffer.from("\n"), L = Buffer.from("'"), O = Buffer.from("z"),
|
|
49
|
+
let x = Array.isArray, F = Buffer.from("\n"), L = Buffer.from("'"), O = Buffer.from("z"), j = L[0], J = O[0];
|
|
50
50
|
|
|
51
51
|
class t {
|
|
52
52
|
snapshotFolder;
|
|
@@ -98,7 +98,7 @@ class t {
|
|
|
98
98
|
outdatedCount: 0
|
|
99
99
|
}), t = (++this.snapshotCount, t > e && (e = t, n = a.maxChangeId),
|
|
100
100
|
this), a = o;
|
|
101
|
-
t =
|
|
101
|
+
t = N(t, a), P.info("Loading " + t), a.prefixPM = await b(t);
|
|
102
102
|
})), Object.assign(this, {
|
|
103
103
|
maxSnapshotNum: e,
|
|
104
104
|
snapshotMaxChangeId: n
|
|
@@ -173,7 +173,7 @@ class t {
|
|
|
173
173
|
}
|
|
174
174
|
S(t, t => {
|
|
175
175
|
t in o || (o[t] = this.getValueSync(t)), this.removeKey(t);
|
|
176
|
-
}),
|
|
176
|
+
}), g(o, t);
|
|
177
177
|
}
|
|
178
178
|
getValueSync(t) {
|
|
179
179
|
var o = ((t, o) => {
|
|
@@ -190,7 +190,7 @@ class t {
|
|
|
190
190
|
posMap: n,
|
|
191
191
|
prefixPM: s
|
|
192
192
|
} = o;
|
|
193
|
-
if (s = s[a]) return [ s, e ] = s, s =
|
|
193
|
+
if (s = s[a]) return [ s, e ] = s, s = T(t, t.getPMFilePath(o), s, e),
|
|
194
194
|
t.loadPartialPosMapBuffer(o, a, s), n;
|
|
195
195
|
})(t, n, s);
|
|
196
196
|
if (i && o in i) return n;
|
|
@@ -207,7 +207,7 @@ class t {
|
|
|
207
207
|
var [ e, a ] = a;
|
|
208
208
|
if (0 != a) {
|
|
209
209
|
var n = this;
|
|
210
|
-
if (o = n.getVLFilePath(o), n =
|
|
210
|
+
if (o = n.getVLFilePath(o), n = T(n, o, e, a)) return (o = n[0]) == j ? n.toString("utf8", 1) : o == J ? m.zstdDecompressSync(n.subarray(1)).toString() : (e = JSON.parse(n),
|
|
211
211
|
I(t) ? f(e) : e);
|
|
212
212
|
}
|
|
213
213
|
}
|
|
@@ -218,7 +218,7 @@ class t {
|
|
|
218
218
|
posMap: e,
|
|
219
219
|
prefixPM: n
|
|
220
220
|
} = a, s = (delete n[t], this).keyToSnapshotMap;
|
|
221
|
-
|
|
221
|
+
V(o, (t, o) => {
|
|
222
222
|
t in s ? ++a.outdatedCount : (e[t] = o, s[t] = a);
|
|
223
223
|
});
|
|
224
224
|
}
|
|
@@ -227,25 +227,25 @@ class t {
|
|
|
227
227
|
o(this, this.getPMFilePath(t));
|
|
228
228
|
}
|
|
229
229
|
calOutdates() {
|
|
230
|
-
var t, o = [],
|
|
231
|
-
options:
|
|
232
|
-
numToSnapshotMap:
|
|
230
|
+
var t, o, a, e = [], n = [], s = [], r = [], {
|
|
231
|
+
options: i,
|
|
232
|
+
numToSnapshotMap: p
|
|
233
233
|
} = this, {
|
|
234
|
-
pMOutdatedPercent:
|
|
235
|
-
vLOutdatedPercent:
|
|
236
|
-
} =
|
|
237
|
-
for (t in
|
|
238
|
-
var
|
|
239
|
-
|
|
240
|
-
|
|
234
|
+
pMOutdatedPercent: u,
|
|
235
|
+
vLOutdatedPercent: f
|
|
236
|
+
} = i;
|
|
237
|
+
for (t in p) {
|
|
238
|
+
var h, l = p[t];
|
|
239
|
+
l.isOut || (100 == (h = Q(l)) ? (this.delSnapshot(l), e.push([ t, l.info.verList[0] ])) : f < h ? (B(l) ? n : r).push(l) : Math.floor(l.outdatedCount / l.info.totalCount * 100) > u ? s.push(l) : (h = this,
|
|
240
|
+
l.info.totalCount < h.options.minSnapshotKeyCount && B(l) && n.push(l)));
|
|
241
241
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
{
|
|
245
|
-
|
|
246
|
-
combiningSnapshotList:
|
|
247
|
-
pMCompactSnapshotList:
|
|
248
|
-
vLCompactSnapshotList:
|
|
242
|
+
1 == n.length && Q(n[0]) < f && (n.length = 0);
|
|
243
|
+
for ([ o, a ] of [ [ "pMCompacts", s ], [ "vLCompacts", r ], [ "combiningSnapshots", n ] ]) 0 < a.length && P.log(o + ":" + a.map(t => t.snapshotNum + "." + t.info.verList[0]));
|
|
244
|
+
return {
|
|
245
|
+
emptySnapshotNumVerList: e,
|
|
246
|
+
combiningSnapshotList: n,
|
|
247
|
+
pMCompactSnapshotList: s,
|
|
248
|
+
vLCompactSnapshotList: r
|
|
249
249
|
};
|
|
250
250
|
}
|
|
251
251
|
getMaxCombinedSnapshot(t) {
|
|
@@ -278,7 +278,7 @@ class t {
|
|
|
278
278
|
outdatedCount: r,
|
|
279
279
|
totalCount: i
|
|
280
280
|
} = o, p = s[0], s = s[0] = e + n;
|
|
281
|
-
return o.outdated = Math.floor(100 * r / i) + "%", P.log(`Saving snapshotInfo:${t}
|
|
281
|
+
return o.outdated = Math.floor(100 * r / i) + "%", P.log(`Saving snapshotInfo:${p ? t + `.${p}->` : ""}${t}.` + s),
|
|
282
282
|
!!await a.saveToFile({
|
|
283
283
|
...o,
|
|
284
284
|
verList: void 0
|
|
@@ -295,12 +295,12 @@ class t {
|
|
|
295
295
|
for (a of o = o || Object.keys(s).sort()) {
|
|
296
296
|
null == f ? f = w(a) : a.startsWith(f) || (i[f] = [ p, u ], f = w(a),
|
|
297
297
|
p += u, u = 0);
|
|
298
|
-
var h = Buffer.from(a), l =
|
|
298
|
+
var h = Buffer.from(a), l = c(s[a]);
|
|
299
299
|
r.save([ h, l ]), u += h.length + l.length;
|
|
300
300
|
}
|
|
301
301
|
return null != f && (i[f] = [ p, u ]), Promise.all([ r.stop(), ((t, o) => {
|
|
302
|
-
var a, e = new M(), n = (e.start(
|
|
303
|
-
for (a in n) e.save([ Buffer.from(a),
|
|
302
|
+
var a, e = new M(), n = (e.start(N(t, o)), o).prefixPM;
|
|
303
|
+
for (a in n) e.save([ Buffer.from(a), c(n[a]) ]);
|
|
304
304
|
return e.stop();
|
|
305
305
|
})(this, t) ]);
|
|
306
306
|
}
|
|
@@ -320,11 +320,11 @@ class t {
|
|
|
320
320
|
}
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
-
function
|
|
323
|
+
function V(o, a) {
|
|
324
324
|
var e = o.length;
|
|
325
325
|
for (let t = 0; t < e; ) {
|
|
326
326
|
var n = t + u, s = o.toString("latin1", t, n), r = [];
|
|
327
|
-
t =
|
|
327
|
+
t = y(o, n, r), a(s, r);
|
|
328
328
|
}
|
|
329
329
|
}
|
|
330
330
|
|
|
@@ -332,12 +332,12 @@ async function b(t) {
|
|
|
332
332
|
var o = await r(t), a = o.length, e = {};
|
|
333
333
|
for (let t = 0; t < a; ) {
|
|
334
334
|
var n = t + k, s = e[o.toString("latin1", t, n)] = [];
|
|
335
|
-
t =
|
|
335
|
+
t = y(o, n, s);
|
|
336
336
|
}
|
|
337
337
|
return e;
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
-
function
|
|
340
|
+
function T(t, o, a, e) {
|
|
341
341
|
var n = ((t, o) => {
|
|
342
342
|
let a = t.fDMap, e = a[o];
|
|
343
343
|
if (!e) {
|
|
@@ -358,7 +358,7 @@ function N(t, o, a, e) {
|
|
|
358
358
|
return s;
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
-
function
|
|
361
|
+
function B(t) {
|
|
362
362
|
var {
|
|
363
363
|
keyValueMap: o,
|
|
364
364
|
prefixPM: a
|
|
@@ -366,7 +366,7 @@ function T(t) {
|
|
|
366
366
|
return o || null == a || h(t.prefixPM);
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
-
function
|
|
369
|
+
function N(t, o) {
|
|
370
370
|
var {
|
|
371
371
|
snapshotNum: o,
|
|
372
372
|
info: a
|
|
@@ -403,7 +403,7 @@ function D(t, o) {
|
|
|
403
403
|
|
|
404
404
|
export {
|
|
405
405
|
t as QySnapshots,
|
|
406
|
-
|
|
406
|
+
V as iterPMBuffer,
|
|
407
407
|
b as loadPrefixPMFile,
|
|
408
|
-
D as
|
|
408
|
+
D as getInfoFileName
|
|
409
409
|
};
|
package/src/simulate2.js
CHANGED
|
@@ -101,7 +101,7 @@ function b(t) {
|
|
|
101
101
|
await !console.timeEnd("loadData"), console.log(`airports:${d.length}, flights:` + h.length);
|
|
102
102
|
var s = new n(p, {
|
|
103
103
|
fileLogLevel: "basic",
|
|
104
|
-
maxInMemSnapshotCount:
|
|
104
|
+
maxInMemSnapshotCount: 3
|
|
105
105
|
}), r = (t && (await s.start(), b(s), await s.stop()), await s.start(), g(s),
|
|
106
106
|
`simulate ${e} ` + o), i = (console.time(r), console.time("simulateStop"), []);
|
|
107
107
|
for (let t = 0; t < e; ++t) i.push(y(s, o));
|