@iobroker/db-objects-jsonl 4.0.0-alpha.45-20220114-88aaebdb → 4.0.0-alpha.49-20220121-e7aa4308

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.
@@ -54,7 +54,9 @@ class ObjectsInMemoryJsonlDB extends ObjectsInMemoryFileDB {
54
54
  }
55
55
 
56
56
  async open() {
57
- await this._db.open();
57
+ if (!(await this._maybeMigrateFileDB())) {
58
+ await this._db.open();
59
+ }
58
60
 
59
61
  // Create an object-like wrapper around the internal Map
60
62
  this.dataset = new Proxy(this._db, {
@@ -99,6 +101,82 @@ class ObjectsInMemoryJsonlDB extends ObjectsInMemoryFileDB {
99
101
  }
100
102
  }
101
103
 
104
+ /**
105
+ * Checks if an existing file DB should be migrated to JSONL
106
+ * @returns {Promise<boolean>} true if the file DB was migrated. false if not.
107
+ * If this returns true, the jsonl DB was opened and doesn't need to be opened again.
108
+ */
109
+ async _maybeMigrateFileDB() {
110
+ const jsonlFileName = path.join(this.dataDir, this.settings.jsonlDB.fileName);
111
+ const jsonFileName = path.join(this.dataDir, this.settings.fileDB.fileName);
112
+ const bakFileName = path.join(this.dataDir, this.settings.fileDB.fileName + '.bak');
113
+
114
+ // Check the timestamps of each file, defaulting to 0 if they don't exist
115
+ let jsonlTimeStamp = 0;
116
+ let jsonTimeStamp = 0;
117
+ let bakTimeStamp = 0;
118
+ try {
119
+ const stat = fs.statSync(jsonlFileName);
120
+ if (stat.isFile()) {
121
+ jsonlTimeStamp = stat.mtime;
122
+ }
123
+ } catch {
124
+ // ignore
125
+ }
126
+ try {
127
+ const stat = fs.statSync(jsonFileName);
128
+ if (stat.isFile()) {
129
+ jsonTimeStamp = stat.mtime;
130
+ }
131
+ } catch {
132
+ // ignore
133
+ }
134
+ try {
135
+ const stat = fs.statSync(bakFileName);
136
+ if (stat.isFile()) {
137
+ bakTimeStamp = stat.mtime;
138
+ }
139
+ } catch {
140
+ // ignore
141
+ }
142
+
143
+ // Figure out which file needs to be imported
144
+ /** @type {string} */
145
+ let importFilename;
146
+ if (jsonTimeStamp > 0 && jsonTimeStamp >= bakTimeStamp && jsonTimeStamp >= jsonlTimeStamp) {
147
+ importFilename = jsonFileName;
148
+ } else if (bakTimeStamp > 0 && bakTimeStamp >= jsonTimeStamp && bakTimeStamp >= jsonlTimeStamp) {
149
+ importFilename = bakFileName;
150
+ } else {
151
+ // None of the File DB files are newer than the JSONL file
152
+ // There is nothing to restore, we're done
153
+ return false;
154
+ }
155
+
156
+ await this._db.open();
157
+ this._db.clear();
158
+ await this._db.importJson(importFilename);
159
+
160
+ // And rename the existing files to avoid redoing the work next time
161
+ if (fs.existsSync(jsonFileName)) {
162
+ try {
163
+ fs.renameSync(jsonFileName, `${jsonFileName}.migrated`);
164
+ } catch {
165
+ // ignore
166
+ }
167
+ }
168
+ if (fs.existsSync(bakFileName)) {
169
+ try {
170
+ fs.renameSync(bakFileName, `${bakFileName}.migrated`);
171
+ } catch {
172
+ // ignore
173
+ }
174
+ }
175
+
176
+ // Signal to the caller that the DB is already open
177
+ return true;
178
+ }
179
+
102
180
  async saveState() {
103
181
  // Nothing to do, the DB saves behind the scenes
104
182
  }
@@ -106,10 +184,10 @@ class ObjectsInMemoryJsonlDB extends ObjectsInMemoryFileDB {
106
184
  // Is regularly called and stores a compressed backup of the DB
107
185
  async saveBackup() {
108
186
  const now = Date.now();
109
- const tmpBackupFileName = path.join(os.tmpdir(), this.getTimeStr(now) + '_' + this.settings.jsonlDB.fileName);
187
+ const tmpBackupFileName = path.join(os.tmpdir(), `${this.getTimeStr(now)}_${this.settings.jsonlDB.fileName}`);
110
188
  const backupFileName = path.join(
111
189
  this.backupDir,
112
- this.getTimeStr(now) + '_' + this.settings.jsonlDB.fileName + '.gz'
190
+ `${this.getTimeStr(now)}_${this.settings.jsonlDB.fileName}.gz`
113
191
  );
114
192
  try {
115
193
  if (fs.existsSync(backupFileName)) {
@@ -294,11 +294,22 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
294
294
  );
295
295
  }
296
296
  handler.sendBulk(responseId, scriptChecksum);
297
+ } else if (data[1].includes('-- REDLOCK SCRIPT')) {
298
+ // redlock scripts are currently not needed for Simulator
299
+ this.knownScripts[scriptChecksum] = { redlock: true };
300
+ if (this.settings.connection.enhancedLogging) {
301
+ this.log.silly(
302
+ `${namespaceLog} Register Func LUA Script: ${scriptChecksum} = ${JSON.stringify(
303
+ this.knownScripts[scriptChecksum]
304
+ )}`
305
+ );
306
+ }
307
+ handler.sendBulk(responseId, scriptChecksum);
297
308
  } else {
298
- handler.sendError(responseId, new Error('Unknown LUA script ' + data[0]));
309
+ handler.sendError(responseId, new Error(`Unknown LUA script ${data[1]}`));
299
310
  }
300
311
  } else {
301
- handler.sendError(responseId, new Error('Unsupported Script command ' + data[0]));
312
+ handler.sendError(responseId, new Error(`Unsupported Script command ${data[0]}`));
302
313
  }
303
314
  });
304
315
 
@@ -353,8 +364,11 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
353
364
  const res = objs.rows.map(obj => JSON.stringify(this.dataset[obj.value._id || obj.id]));
354
365
 
355
366
  return void handler.sendArray(responseId, res);
367
+ } else if (this.knownScripts[data[0]].redlock) {
368
+ // just return a dummy
369
+ return void handler.sendArray(responseId, [0]);
356
370
  } else {
357
- handler.sendError(responseId, new Error('Unknown LUA script eval call ' + JSON.stringify(data)));
371
+ handler.sendError(responseId, new Error(`Unknown LUA script eval call ${JSON.stringify(data)}`));
358
372
  }
359
373
  });
360
374
 
@@ -521,11 +535,17 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
521
535
  handler.sendBufBulk(responseId, Buffer.from(fileData));
522
536
  }
523
537
  } else if (namespace === this.metaNamespace) {
524
- const result = this.getMeta(id);
525
- if (result === undefined || result === null) {
526
- handler.sendNull(responseId);
538
+ // special handling for the primaryHost
539
+ if (id === 'objects.primaryHost') {
540
+ // we are the server -> we are primary
541
+ handler.sendString(this.settings.hostname);
527
542
  } else {
528
- handler.sendBulk(responseId, result);
543
+ const result = this.getMeta(id);
544
+ if (result === undefined || result === null) {
545
+ handler.sendNull(responseId);
546
+ } else {
547
+ handler.sendBulk(responseId, result);
548
+ }
529
549
  }
530
550
  } else {
531
551
  handler.sendError(
@@ -710,15 +730,6 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
710
730
  return this._handleScanOrKeys(handler, data[0], responseId);
711
731
  });
712
732
 
713
- // MULTI/EXEC is never used with return values, thus we just answer with syntactic correct responses
714
- handler.on('multi', (data, responseId) => {
715
- return void handler.sendString(responseId, 'OK');
716
- });
717
-
718
- handler.on('exec', (data, reponseId) => {
719
- return void handler.sendArray(reponseId, []);
720
- });
721
-
722
733
  // commands for redis SETS, just dummies
723
734
  handler.on('sadd', (data, responseId) => {
724
735
  return void handler.sendInteger(responseId, 1);
@@ -770,9 +781,22 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
770
781
  }
771
782
  });
772
783
 
784
+ // Handle Redis "SUBSCRIBE" ... currently mainly ignored
785
+ handler.on('subscribe', (data, responseId) => {
786
+ if (data[0].startsWith('__keyevent@')) {
787
+ // we ignore these type of events because we publish expires anyway directly
788
+ handler.sendArray(responseId, ['subscribe', data[0], 1]);
789
+ } else {
790
+ handler.sendError(responseId, new Error(`SUBSCRIBE-UNSUPPORTED for ${data[0]}`));
791
+ }
792
+ });
793
+
773
794
  // Handle Redis "CONFIG" ... currently mainly ignored
774
795
  handler.on('config', (data, responseId) => {
775
- if (data[0] === 'set' && data[1] === 'lua-time-limit') {
796
+ if (data[0] === 'set' && data[1] === 'notify-keyspace-events') {
797
+ // we ignore these type of commands for now, should only be to subscribe to keyspace events
798
+ handler.sendString(responseId, 'OK');
799
+ } else if (data[0] === 'set' && data[1] === 'lua-time-limit') {
776
800
  // we ignore these type of commands for now, irrelevant
777
801
  handler.sendString(responseId, 'OK');
778
802
  } else {
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@iobroker/db-objects-jsonl",
3
- "version": "4.0.0-alpha.45-20220114-88aaebdb",
3
+ "version": "4.0.0-alpha.49-20220121-e7aa4308",
4
4
  "engines": {
5
5
  "node": ">=12.0.0"
6
6
  },
7
7
  "dependencies": {
8
8
  "@alcalzone/jsonl-db": "~2.4.1",
9
- "@iobroker/db-base": "4.0.0-alpha.45-20220114-88aaebdb",
10
- "@iobroker/db-objects-file": "4.0.0-alpha.45-20220114-88aaebdb",
11
- "@iobroker/db-objects-redis": "4.0.0-alpha.45-20220114-88aaebdb",
9
+ "@iobroker/db-base": "4.0.0-alpha.49-20220121-e7aa4308",
10
+ "@iobroker/db-objects-file": "4.0.0-alpha.49-20220121-e7aa4308",
11
+ "@iobroker/db-objects-redis": "4.0.0-alpha.49-20220121-e7aa4308",
12
12
  "deep-clone": "^3.0.3",
13
13
  "fs-extra": "^10.0.0"
14
14
  },
@@ -37,5 +37,5 @@
37
37
  "lib/",
38
38
  "index.js"
39
39
  ],
40
- "gitHead": "e2fd3e7af91eb83c30f8ae45f71d6e178edc7b28"
40
+ "gitHead": "f42f117b74d3c4bfac0bc872c087d382ca897b7c"
41
41
  }