-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmultisync.js
More file actions
62 lines (57 loc) · 2.04 KB
/
Copy pathmultisync.js
File metadata and controls
62 lines (57 loc) · 2.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/* ==============================================
* MULTISYNC — many backends (<=50 col house)
* one shape. Sync to InstantDB AND Pear AND memory
* at once. Same { publish, subscribe } interface,
* so the mesh cannot tell it is talking to a crowd.
* ----------------------------------------------
* WRITE: fan the event to every backend. Each gets
* a timeout; a slow/dead backend cannot stall the
* others (allSettled + race-with-timeout).
* READ: subscribe to every backend, but DEDUP by
* event._id so the caller sees each fact once,
* whichever network delivered it first. That IS
* the consolidation: the fastest path wins, the
* rest are no-ops.
* ============================================ */
'use strict';
function withTimeout(promise, ms, label) {
let t;
const timeout = new Promise((_, rej) => {
t = setTimeout(() =>
rej(new Error('timeout: ' + label)), ms);
});
return Promise.race([promise, timeout])
.finally(() => clearTimeout(t));
}
// adapters: [{ name, sync }] sync = {publish,subscribe}
function combineSync(adapters, opts) {
opts = opts || {};
const ms = opts.timeoutMs || 3000;
const seen = new Set(); // dedup by _id
const report = opts.onResult || (() => {});
return {
publish: async event => {
const jobs = adapters.map(a =>
withTimeout(
Promise.resolve(a.sync.publish(event)),
ms, a.name)
.then(() => ({ name: a.name, ok: true }))
.catch(e => ({ name: a.name,
ok: false, err: e.message })));
const results = await Promise.allSettled(jobs);
const flat = results.map(r => r.value || r.reason);
report(event._id, flat);
return flat; // never throws
},
subscribe: cb => {
const offs = adapters.map(a =>
a.sync.subscribe(event => {
if (seen.has(event._id)) return;
seen.add(event._id);
cb(event);
}));
return () => offs.forEach(off => off && off());
},
};
}
module.exports = { combineSync, withTimeout };