antietcd 1.0.8 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/anticluster.js +15 -6
- package/antietcd-app.js +9 -5
- package/antietcd.js +90 -12
- package/antipersistence.js +7 -9
- package/common.js +8 -0
- package/etctree.js +26 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -105,6 +105,9 @@ Specify <ca> = <cert> if your certificate is self-signed.</dd>
|
|
|
105
105
|
<dt>--ws_keepalive_interval 30000</dt>
|
|
106
106
|
<dd>Client websocket ping (keepalive) interval in milliseconds</dd>
|
|
107
107
|
|
|
108
|
+
<dt>--use_base64 1</dt>
|
|
109
|
+
<dd>Use base64 encoding of keys and values, like in etcd (enabled by default).</dd>
|
|
110
|
+
|
|
108
111
|
</dl>
|
|
109
112
|
|
|
110
113
|
### Persistence
|
package/anticluster.js
CHANGED
|
@@ -226,7 +226,10 @@ class AntiCluster
|
|
|
226
226
|
}
|
|
227
227
|
else
|
|
228
228
|
{
|
|
229
|
-
this._log(
|
|
229
|
+
this._log(
|
|
230
|
+
'Got dump from '+client.raft_node_id+' with stored term '+res.term+
|
|
231
|
+
', mod_revision '+res.mod_revision+', compact_revision '+res.compact_revision
|
|
232
|
+
);
|
|
230
233
|
}
|
|
231
234
|
this.resync_state.dumps[client.raft_node_id] = res.error ? null : res;
|
|
232
235
|
this._continueResync();
|
|
@@ -325,16 +328,19 @@ class AntiCluster
|
|
|
325
328
|
this.antietcd.stored_term = this.raft.term;
|
|
326
329
|
this.synced = true;
|
|
327
330
|
runCallbacks(this, 'wait_sync', []);
|
|
328
|
-
this._log(
|
|
331
|
+
this._log(
|
|
332
|
+
'Synchronized with followers, new term is '+this.raft.term+
|
|
333
|
+
', mod_revision '+this.antietcd.etctree.mod_revision+', compact_revision '+this.antietcd.etctree.compact_revision
|
|
334
|
+
);
|
|
329
335
|
}
|
|
330
336
|
|
|
331
337
|
_isWrite(path, data)
|
|
332
338
|
{
|
|
333
339
|
if (path == 'kv_txn')
|
|
334
340
|
{
|
|
335
|
-
return (
|
|
336
|
-
|
|
337
|
-
|
|
341
|
+
return (data.compare && data.compare.length ||
|
|
342
|
+
data.success && data.success.filter(f => f.request_put || f.requestPut || f.request_delete_range || f.requestDeleteRange).length ||
|
|
343
|
+
data.failure && data.failure.filter(f => f.request_put || f.requestPut || f.request_delete_range || f.requestDeleteRange).length);
|
|
338
344
|
}
|
|
339
345
|
return path != 'kv_range';
|
|
340
346
|
}
|
|
@@ -452,7 +458,10 @@ class AntiCluster
|
|
|
452
458
|
this.antietcd.stored_term = msg.term;
|
|
453
459
|
this.synced = true;
|
|
454
460
|
runCallbacks(this, 'wait_sync', []);
|
|
455
|
-
this._log(
|
|
461
|
+
this._log(
|
|
462
|
+
'Synchronized with leader, new term is '+this.raft.term+
|
|
463
|
+
', mod_revision '+this.antietcd.etctree.mod_revision+', compact_revision '+this.antietcd.etctree.compact_revision
|
|
464
|
+
);
|
|
456
465
|
client.socket.send(JSON.stringify({ request_id: msg.request_id, reply: {} }));
|
|
457
466
|
}
|
|
458
467
|
else
|
package/antietcd-app.js
CHANGED
|
@@ -12,10 +12,10 @@ License: Mozilla Public License 2.0 or Vitastor Network Public License 1.1
|
|
|
12
12
|
|
|
13
13
|
Usage:
|
|
14
14
|
|
|
15
|
-
${process.argv[0]} ${process.argv[1]} \
|
|
16
|
-
[--cert ssl.crt] [--key ssl.key] [--port 12379] \
|
|
17
|
-
[--data data.gz] [--persist-filter ./filter.js] [--persist_interval 500] \
|
|
18
|
-
[--node_id node1 --cluster_key abcdef --cluster node1=http://localhost:12379,node2=http://localhost:12380,node3=http://localhost:12381] \
|
|
15
|
+
${process.argv[0]} ${process.argv[1]} \n\
|
|
16
|
+
[--cert ssl.crt] [--key ssl.key] [--port 12379] \n\
|
|
17
|
+
[--data data.gz] [--persist-filter ./filter.js] [--persist_interval 500] \n\
|
|
18
|
+
[--node_id node1 --cluster_key abcdef --cluster node1=http://localhost:12379,node2=http://localhost:12380,node3=http://localhost:12381] \n\
|
|
19
19
|
[other options]
|
|
20
20
|
|
|
21
21
|
Supported etcd REST APIs:
|
|
@@ -43,6 +43,11 @@ HTTP:
|
|
|
43
43
|
Require TLS client certificates signed by <ca> or by default CA to connect.
|
|
44
44
|
--ws_keepalive_interval 30000
|
|
45
45
|
Client websocket ping (keepalive) interval in milliseconds
|
|
46
|
+
--merge_watches 1
|
|
47
|
+
Antietcd merges all watcher events into a single websocket message to provide
|
|
48
|
+
more ordering/transaction guarantees. Set to 0 to disable this behaviour.
|
|
49
|
+
--use_base64 1
|
|
50
|
+
Use base64 encoding of keys and values, like in etcd (enabled by default).
|
|
46
51
|
|
|
47
52
|
Persistence:
|
|
48
53
|
|
|
@@ -105,7 +110,6 @@ function parse()
|
|
|
105
110
|
options[arg.substr(2)] = process.argv[++i];
|
|
106
111
|
}
|
|
107
112
|
}
|
|
108
|
-
options['stale_read'] = options['stale_read'] === '1' || options['stale_read'] === 'yes' || options['stale_read'] === 'true';
|
|
109
113
|
if (options['persist_filter'])
|
|
110
114
|
{
|
|
111
115
|
options['persist_filter'] = require(options['persist_filter'])(options);
|
package/antietcd.js
CHANGED
|
@@ -15,15 +15,18 @@ const ws = require('ws');
|
|
|
15
15
|
const EtcTree = require('./etctree.js');
|
|
16
16
|
const AntiPersistence = require('./antipersistence.js');
|
|
17
17
|
const AntiCluster = require('./anticluster.js');
|
|
18
|
-
const { runCallbacks, RequestError } = require('./common.js');
|
|
18
|
+
const { runCallbacks, de64, b64, RequestError } = require('./common.js');
|
|
19
19
|
|
|
20
|
-
const VERSION = '1.0
|
|
20
|
+
const VERSION = '1.1.0';
|
|
21
21
|
|
|
22
22
|
class AntiEtcd extends EventEmitter
|
|
23
23
|
{
|
|
24
24
|
constructor(cfg)
|
|
25
25
|
{
|
|
26
26
|
super();
|
|
27
|
+
cfg['merge_watches'] = !('merge_watches' in cfg) || is_true(cfg['merge_watches']);
|
|
28
|
+
cfg['stale_read'] = !('stale_read' in cfg) || is_true(cfg['stale_read']);
|
|
29
|
+
cfg['use_base64'] = !('use_base64' in cfg) || is_true(cfg['use_base64']);
|
|
27
30
|
this.clients = {};
|
|
28
31
|
this.client_id = 1;
|
|
29
32
|
this.etctree = new EtcTree(true);
|
|
@@ -74,7 +77,7 @@ class AntiEtcd extends EventEmitter
|
|
|
74
77
|
{
|
|
75
78
|
this.server = http.createServer((req, res) => this._handleRequest(req, res));
|
|
76
79
|
}
|
|
77
|
-
this.wss = new ws.
|
|
80
|
+
this.wss = new ws.Server({ server: this.server });
|
|
78
81
|
// eslint-disable-next-line no-unused-vars
|
|
79
82
|
this.wss.on('connection', (conn, req) => this._startWebsocket(conn, null));
|
|
80
83
|
this.server.listen(this.cfg.port || 2379, this.cfg.ip || undefined);
|
|
@@ -120,7 +123,7 @@ class AntiEtcd extends EventEmitter
|
|
|
120
123
|
let done = 0;
|
|
121
124
|
await new Promise((allOk, allNo) =>
|
|
122
125
|
{
|
|
123
|
-
res.map(promise => promise.then(
|
|
126
|
+
res.map(promise => promise.then(r =>
|
|
124
127
|
{
|
|
125
128
|
if ((++done) == res.length)
|
|
126
129
|
allOk();
|
|
@@ -411,9 +414,9 @@ class AntiEtcd extends EventEmitter
|
|
|
411
414
|
}
|
|
412
415
|
|
|
413
416
|
// public watch API
|
|
414
|
-
async create_watch(params, callback)
|
|
417
|
+
async create_watch(params, callback, stream_id)
|
|
415
418
|
{
|
|
416
|
-
const watch = this.etctree.api_create_watch(
|
|
419
|
+
const watch = this.etctree.api_create_watch(this._encodeWatch(params), (msg) => callback(this._encodeMsg(msg)), stream_id);
|
|
417
420
|
if (!watch.created)
|
|
418
421
|
{
|
|
419
422
|
throw new RequestError(400, 'Requested watch revision is compacted', { compact_revision: watch.compact_revision });
|
|
@@ -437,24 +440,64 @@ class AntiEtcd extends EventEmitter
|
|
|
437
440
|
// internal handlers
|
|
438
441
|
async _handle_kv_txn(data)
|
|
439
442
|
{
|
|
440
|
-
|
|
443
|
+
if (this.cfg.use_base64)
|
|
444
|
+
{
|
|
445
|
+
for (const item of data.compare||[])
|
|
446
|
+
{
|
|
447
|
+
if (item.key != null)
|
|
448
|
+
item.key = de64(item.key);
|
|
449
|
+
}
|
|
450
|
+
for (const items of [ data.success, data.failure ])
|
|
451
|
+
{
|
|
452
|
+
for (const item of items||[])
|
|
453
|
+
{
|
|
454
|
+
const req = item.request_range || item.requestRange ||
|
|
455
|
+
item.request_put || item.requestPut ||
|
|
456
|
+
item.request_delete_range || item.requestDeleteRange;
|
|
457
|
+
if (req.key != null)
|
|
458
|
+
req.key = de64(req.key);
|
|
459
|
+
if (req.range_end != null)
|
|
460
|
+
req.range_end = de64(req.range_end);
|
|
461
|
+
if (req.value != null)
|
|
462
|
+
req.value = de64(req.value);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
const result = await this.etctree.api_txn(data);
|
|
467
|
+
if (this.cfg.use_base64)
|
|
468
|
+
{
|
|
469
|
+
for (const item of result.responses||[])
|
|
470
|
+
{
|
|
471
|
+
if (item.response_range)
|
|
472
|
+
{
|
|
473
|
+
for (const kv of item.response_range.kvs)
|
|
474
|
+
{
|
|
475
|
+
if (kv.key != null)
|
|
476
|
+
kv.key = b64(kv.key);
|
|
477
|
+
if (kv.value != null)
|
|
478
|
+
kv.value = b64(kv.value);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return result;
|
|
441
484
|
}
|
|
442
485
|
|
|
443
486
|
async _handle_kv_range(data)
|
|
444
487
|
{
|
|
445
|
-
const r = await this.
|
|
488
|
+
const r = await this._handle_kv_txn({ success: [ { request_range: data } ] });
|
|
446
489
|
return { header: r.header, ...r.responses[0].response_range };
|
|
447
490
|
}
|
|
448
491
|
|
|
449
492
|
async _handle_kv_put(data)
|
|
450
493
|
{
|
|
451
|
-
const r = await this.
|
|
494
|
+
const r = await this._handle_kv_txn({ success: [ { request_put: data } ] });
|
|
452
495
|
return { header: r.header, ...r.responses[0].response_put };
|
|
453
496
|
}
|
|
454
497
|
|
|
455
498
|
async _handle_kv_deleterange(data)
|
|
456
499
|
{
|
|
457
|
-
const r = await this.
|
|
500
|
+
const r = await this._handle_kv_txn({ success: [ { request_delete_range: data } ] });
|
|
458
501
|
return { header: r.header, ...r.responses[0].response_delete_range };
|
|
459
502
|
}
|
|
460
503
|
|
|
@@ -478,7 +521,7 @@ class AntiEtcd extends EventEmitter
|
|
|
478
521
|
return this.etctree.api_keepalive_lease(data);
|
|
479
522
|
}
|
|
480
523
|
|
|
481
|
-
_handle_maintenance_status(data)
|
|
524
|
+
_handle_maintenance_status(/*data*/)
|
|
482
525
|
{
|
|
483
526
|
const raft = this.cluster && this.cluster.raft;
|
|
484
527
|
return {
|
|
@@ -516,8 +559,9 @@ class AntiEtcd extends EventEmitter
|
|
|
516
559
|
const create_request = msg.create_request;
|
|
517
560
|
if (!create_request.watch_id || !client.watches[create_request.watch_id])
|
|
518
561
|
{
|
|
562
|
+
client.send_cb = client.send_cb || (msg => socket.send(JSON.stringify(this._encodeMsg(msg))));
|
|
519
563
|
const watch = this.etctree.api_create_watch(
|
|
520
|
-
|
|
564
|
+
this._encodeWatch(create_request), client.send_cb, (this.cfg.merge_watches ? 'C'+client_id : null)
|
|
521
565
|
);
|
|
522
566
|
if (!watch.created)
|
|
523
567
|
{
|
|
@@ -555,6 +599,35 @@ class AntiEtcd extends EventEmitter
|
|
|
555
599
|
}
|
|
556
600
|
}
|
|
557
601
|
|
|
602
|
+
_encodeWatch(create_request)
|
|
603
|
+
{
|
|
604
|
+
const req = { ...create_request, watch_id: null };
|
|
605
|
+
if (this.cfg.use_base64)
|
|
606
|
+
{
|
|
607
|
+
if (req.key != null)
|
|
608
|
+
req.key = de64(req.key);
|
|
609
|
+
if (req.range_end != null)
|
|
610
|
+
req.range_end = de64(req.range_end);
|
|
611
|
+
}
|
|
612
|
+
return req;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
_encodeMsg(msg)
|
|
616
|
+
{
|
|
617
|
+
if (this.cfg.use_base64 && msg.result && msg.result.events)
|
|
618
|
+
{
|
|
619
|
+
return { ...msg, result: { ...msg.result, events: msg.result.events.map(ev => ({
|
|
620
|
+
...ev,
|
|
621
|
+
kv: !ev.kv ? ev.kv : {
|
|
622
|
+
...ev.kv,
|
|
623
|
+
key: b64(ev.kv.key),
|
|
624
|
+
value: b64(ev.kv.value),
|
|
625
|
+
},
|
|
626
|
+
})) } };
|
|
627
|
+
}
|
|
628
|
+
return msg;
|
|
629
|
+
}
|
|
630
|
+
|
|
558
631
|
_unsubscribeClient(client_id)
|
|
559
632
|
{
|
|
560
633
|
if (!this.clients[client_id])
|
|
@@ -569,6 +642,11 @@ class AntiEtcd extends EventEmitter
|
|
|
569
642
|
}
|
|
570
643
|
}
|
|
571
644
|
|
|
645
|
+
function is_true(s)
|
|
646
|
+
{
|
|
647
|
+
return s === true || s === 1 || s === '1' || s === 'yes' || s === 'true' || s === 'on';
|
|
648
|
+
}
|
|
649
|
+
|
|
572
650
|
AntiEtcd.RequestError = RequestError;
|
|
573
651
|
|
|
574
652
|
AntiEtcd.VERSION = VERSION;
|
package/antipersistence.js
CHANGED
|
@@ -8,7 +8,7 @@ const zlib = require('zlib');
|
|
|
8
8
|
|
|
9
9
|
const stableStringify = require('./stable-stringify.js');
|
|
10
10
|
const EtcTree = require('./etctree.js');
|
|
11
|
-
const {
|
|
11
|
+
const { runCallbacks } = require('./common.js');
|
|
12
12
|
|
|
13
13
|
class AntiPersistence
|
|
14
14
|
{
|
|
@@ -60,20 +60,18 @@ class AntiPersistence
|
|
|
60
60
|
if (ev.lease)
|
|
61
61
|
{
|
|
62
62
|
// Values with lease are never persisted
|
|
63
|
-
|
|
64
|
-
if (this.prev_value[key] !== undefined)
|
|
63
|
+
if (this.prev_value[ev.key] !== undefined)
|
|
65
64
|
{
|
|
66
|
-
delete this.prev_value[key];
|
|
65
|
+
delete this.prev_value[ev.key];
|
|
67
66
|
changed = true;
|
|
68
67
|
}
|
|
69
68
|
}
|
|
70
69
|
else
|
|
71
70
|
{
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
if (!EtcTree.eq(filtered, this.prev_value[key]))
|
|
71
|
+
const filtered = this.cfg.persist_filter(ev.key, ev.value == null ? undefined : ev.value);
|
|
72
|
+
if (!EtcTree.eq(filtered, this.prev_value[ev.key]))
|
|
75
73
|
{
|
|
76
|
-
this.prev_value[key] = filtered;
|
|
74
|
+
this.prev_value[ev.key] = filtered;
|
|
77
75
|
changed = true;
|
|
78
76
|
}
|
|
79
77
|
}
|
|
@@ -116,7 +114,7 @@ class AntiPersistence
|
|
|
116
114
|
this.wait_persist = [];
|
|
117
115
|
try
|
|
118
116
|
{
|
|
119
|
-
let dump = this.antietcd.etctree.dump(true);
|
|
117
|
+
let dump = this.antietcd.etctree.dump(true, this.cfg.persist_filter);
|
|
120
118
|
dump['term'] = this.antietcd.stored_term;
|
|
121
119
|
dump = stableStringify(dump);
|
|
122
120
|
dump = await new Promise((ok, no) => zlib.gzip(dump, (err, res) => err ? no(err) : ok(res)));
|
package/common.js
CHANGED
|
@@ -19,6 +19,13 @@ function de64(k)
|
|
|
19
19
|
return Buffer.from(k, 'base64').toString();
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
function b64(k)
|
|
23
|
+
{
|
|
24
|
+
if (k == null) // null or undefined
|
|
25
|
+
return k;
|
|
26
|
+
return Buffer.from(k).toString('base64');
|
|
27
|
+
}
|
|
28
|
+
|
|
22
29
|
function runCallbacks(obj, key, new_value)
|
|
23
30
|
{
|
|
24
31
|
const cbs = obj[key];
|
|
@@ -35,5 +42,6 @@ function runCallbacks(obj, key, new_value)
|
|
|
35
42
|
module.exports = {
|
|
36
43
|
RequestError,
|
|
37
44
|
de64,
|
|
45
|
+
b64,
|
|
38
46
|
runCallbacks,
|
|
39
47
|
};
|
package/etctree.js
CHANGED
|
@@ -19,7 +19,7 @@ const { RequestError } = require('./common.js');
|
|
|
19
19
|
|
|
20
20
|
class EtcTree
|
|
21
21
|
{
|
|
22
|
-
constructor(
|
|
22
|
+
constructor()
|
|
23
23
|
{
|
|
24
24
|
this.state = {};
|
|
25
25
|
this.leases = {};
|
|
@@ -27,7 +27,6 @@ class EtcTree
|
|
|
27
27
|
this.watcher_id = 0;
|
|
28
28
|
this.mod_revision = 0;
|
|
29
29
|
this.compact_revision = 0;
|
|
30
|
-
this.use_base64 = use_base64;
|
|
31
30
|
this.replicate = null;
|
|
32
31
|
this.paused = false;
|
|
33
32
|
this.active_immediate = [];
|
|
@@ -51,23 +50,9 @@ class EtcTree
|
|
|
51
50
|
this.replicate = replicate;
|
|
52
51
|
}
|
|
53
52
|
|
|
54
|
-
de64(k)
|
|
55
|
-
{
|
|
56
|
-
if (k == null) // null or undefined
|
|
57
|
-
return k;
|
|
58
|
-
return this.use_base64 ? Buffer.from(k, 'base64').toString() : k;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
b64(k)
|
|
62
|
-
{
|
|
63
|
-
if (k == null) // null or undefined
|
|
64
|
-
return k;
|
|
65
|
-
return this.use_base64 ? Buffer.from(k).toString('base64') : k;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
53
|
_check(chk)
|
|
69
54
|
{
|
|
70
|
-
const parts = this._key_parts(
|
|
55
|
+
const parts = this._key_parts(chk.key);
|
|
71
56
|
const { cur } = this._get_subtree(parts, false, false);
|
|
72
57
|
let check_value, ref_value;
|
|
73
58
|
if (chk.target === 'MOD')
|
|
@@ -118,8 +103,8 @@ class EtcTree
|
|
|
118
103
|
|
|
119
104
|
_get_range(req)
|
|
120
105
|
{
|
|
121
|
-
const key =
|
|
122
|
-
const end =
|
|
106
|
+
const key = req.key;
|
|
107
|
+
const end = req.range_end;
|
|
123
108
|
if (end != null && (key !== '' && end !== '') && (key[key.length-1] != '/' || end[end.length-1] != '0' ||
|
|
124
109
|
end.substr(0, end.length-1) !== key.substr(0, key.length-1)))
|
|
125
110
|
{
|
|
@@ -168,7 +153,7 @@ class EtcTree
|
|
|
168
153
|
dump(persistent_only, value_filter)
|
|
169
154
|
{
|
|
170
155
|
const snapshot = {
|
|
171
|
-
state: this._copy_tree(this.state, persistent_only, value_filter) || {},
|
|
156
|
+
state: this._copy_tree(this.state, null, persistent_only, value_filter) || {},
|
|
172
157
|
mod_revision: this.mod_revision,
|
|
173
158
|
compact_revision: this.compact_revision,
|
|
174
159
|
};
|
|
@@ -184,13 +169,13 @@ class EtcTree
|
|
|
184
169
|
return snapshot;
|
|
185
170
|
}
|
|
186
171
|
|
|
187
|
-
_copy_tree(cur, no_lease, value_filter)
|
|
172
|
+
_copy_tree(cur, key, no_lease, value_filter)
|
|
188
173
|
{
|
|
189
174
|
let nonempty = cur.value != null && (!no_lease || !cur.lease);
|
|
190
175
|
let filtered;
|
|
191
176
|
if (nonempty && value_filter)
|
|
192
177
|
{
|
|
193
|
-
filtered = value_filter(cur.value);
|
|
178
|
+
filtered = value_filter(key === null ? '' : key, cur.value);
|
|
194
179
|
nonempty = nonempty && filtered != null;
|
|
195
180
|
}
|
|
196
181
|
const copy = (nonempty ? { ...cur } : {});
|
|
@@ -204,7 +189,7 @@ class EtcTree
|
|
|
204
189
|
let has_children = false;
|
|
205
190
|
for (const k in cur.children)
|
|
206
191
|
{
|
|
207
|
-
const child = this._copy_tree(cur.children[k], no_lease, value_filter);
|
|
192
|
+
const child = this._copy_tree(cur.children[k], key === null ? k : key+'/'+k, no_lease, value_filter);
|
|
208
193
|
if (child)
|
|
209
194
|
{
|
|
210
195
|
copy.children[k] = child;
|
|
@@ -279,7 +264,7 @@ class EtcTree
|
|
|
279
264
|
{
|
|
280
265
|
cur_old.value = cur_new.value;
|
|
281
266
|
const key_watchers = (cur_old.key_watchers ? [ ...watchers, ...(cur_old.key_watchers||[]) ] : watchers);
|
|
282
|
-
const notify = { watchers: key_watchers, key
|
|
267
|
+
const notify = { watchers: key_watchers, key, value: cur_new.value, mod_revision: cur_new.mod_revision };
|
|
283
268
|
if (cur_new.lease)
|
|
284
269
|
{
|
|
285
270
|
notify.lease = cur_new.lease;
|
|
@@ -444,7 +429,7 @@ class EtcTree
|
|
|
444
429
|
}
|
|
445
430
|
for (const key in this.leases[id].keys)
|
|
446
431
|
{
|
|
447
|
-
this._delete_range({ key
|
|
432
|
+
this._delete_range({ key }, next_revision, notifications);
|
|
448
433
|
}
|
|
449
434
|
if (this.leases[id].timer_id)
|
|
450
435
|
{
|
|
@@ -544,7 +529,7 @@ class EtcTree
|
|
|
544
529
|
}
|
|
545
530
|
}
|
|
546
531
|
|
|
547
|
-
api_create_watch(req, send)
|
|
532
|
+
api_create_watch(req, send, stream_id)
|
|
548
533
|
{
|
|
549
534
|
const { parts, all } = this._get_range(req);
|
|
550
535
|
if (req.start_revision && this.compact_revision && this.compact_revision > req.start_revision)
|
|
@@ -566,6 +551,7 @@ class EtcTree
|
|
|
566
551
|
this.watchers[watch_id] = {
|
|
567
552
|
paths: [],
|
|
568
553
|
send,
|
|
554
|
+
stream_id,
|
|
569
555
|
};
|
|
570
556
|
}
|
|
571
557
|
this.watchers[watch_id].paths.push(parts);
|
|
@@ -602,9 +588,9 @@ class EtcTree
|
|
|
602
588
|
{
|
|
603
589
|
const ev = {
|
|
604
590
|
type: cur.value == null ? 'DELETE' : 'PUT',
|
|
605
|
-
kv: cur.value == null ? { key:
|
|
606
|
-
key:
|
|
607
|
-
value:
|
|
591
|
+
kv: cur.value == null ? { key: (prefix === null ? '' : prefix) } : {
|
|
592
|
+
key: prefix,
|
|
593
|
+
value: cur.value,
|
|
608
594
|
mod_revision: cur.mod_revision,
|
|
609
595
|
},
|
|
610
596
|
};
|
|
@@ -667,15 +653,15 @@ class EtcTree
|
|
|
667
653
|
{
|
|
668
654
|
if (this.watchers[wid])
|
|
669
655
|
{
|
|
670
|
-
|
|
671
|
-
by_watcher[
|
|
656
|
+
const stream_id = this.watchers[wid].stream_id || wid;
|
|
657
|
+
by_watcher[stream_id] = by_watcher[stream_id] || { send: this.watchers[wid].send, events: {} };
|
|
658
|
+
by_watcher[stream_id].events[notif.key] = conv;
|
|
672
659
|
}
|
|
673
660
|
}
|
|
674
661
|
}
|
|
675
|
-
for (const
|
|
662
|
+
for (const stream_id in by_watcher)
|
|
676
663
|
{
|
|
677
|
-
by_watcher[
|
|
678
|
-
this.watchers[wid].send({ result: by_watcher[wid] });
|
|
664
|
+
by_watcher[stream_id].send({ result: { header: { revision: this.mod_revision }, events: Object.values(by_watcher[stream_id].events) } });
|
|
679
665
|
}
|
|
680
666
|
}
|
|
681
667
|
|
|
@@ -732,9 +718,9 @@ class EtcTree
|
|
|
732
718
|
_put(request_put, cur_revision, notifications)
|
|
733
719
|
{
|
|
734
720
|
// FIXME: prev_kv, ignore_value(?), ignore_lease(?)
|
|
735
|
-
const parts = this._key_parts(
|
|
721
|
+
const parts = this._key_parts(request_put.key);
|
|
736
722
|
const key = parts.join('/');
|
|
737
|
-
const value =
|
|
723
|
+
const value = request_put.value;
|
|
738
724
|
const { cur, watchers } = this._get_subtree(parts, true, true);
|
|
739
725
|
if (cur.key_watchers)
|
|
740
726
|
{
|
|
@@ -767,7 +753,7 @@ class EtcTree
|
|
|
767
753
|
cur.create_revision = cur_revision;
|
|
768
754
|
}
|
|
769
755
|
cur.value = value;
|
|
770
|
-
const notify = { watchers, key
|
|
756
|
+
const notify = { watchers, key, value, mod_revision: cur.mod_revision };
|
|
771
757
|
if (cur.lease)
|
|
772
758
|
{
|
|
773
759
|
notify.lease = cur.lease;
|
|
@@ -798,10 +784,10 @@ class EtcTree
|
|
|
798
784
|
}
|
|
799
785
|
if (cur.value != null)
|
|
800
786
|
{
|
|
801
|
-
const item = { key:
|
|
787
|
+
const item = { key: (prefix === null ? '' : prefix) };
|
|
802
788
|
if (!req.keys_only)
|
|
803
789
|
{
|
|
804
|
-
item.value =
|
|
790
|
+
item.value = cur.value;
|
|
805
791
|
item.mod_revision = cur.mod_revision;
|
|
806
792
|
//item.create_revision = cur.create_revision;
|
|
807
793
|
//item.version = cur.version;
|
|
@@ -838,7 +824,7 @@ class EtcTree
|
|
|
838
824
|
this.mod_revision = cur_revision;
|
|
839
825
|
notifications.push({
|
|
840
826
|
watchers: cur.key_watchers ? [ ...watchers, ...cur.key_watchers ] : watchers,
|
|
841
|
-
key:
|
|
827
|
+
key: (prefix === null ? '' : prefix),
|
|
842
828
|
mod_revision: cur_revision,
|
|
843
829
|
});
|
|
844
830
|
}
|