@omegup/msync 0.1.28 → 0.1.30
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/index.esm.js +22 -34
- package/index.js +21 -33
- package/package.json +1 -1
package/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { UUID, Timestamp, MongoClient } from 'mongodb';
|
|
2
2
|
import crypto$1 from 'crypto';
|
|
3
3
|
import { canonicalize } from 'json-canonicalize';
|
|
4
4
|
import { SynchronousPromise } from 'synchronous-promise';
|
|
@@ -82,7 +82,7 @@ const val = (val) => asExpr({
|
|
|
82
82
|
: val),
|
|
83
83
|
});
|
|
84
84
|
const current = asExpr({
|
|
85
|
-
raw: () => asExprRaw(
|
|
85
|
+
raw: () => asExprRaw('$$CLUSTER_TIME'),
|
|
86
86
|
});
|
|
87
87
|
const $let = (vars, inExpr) => asExpr({
|
|
88
88
|
raw: f => asExprRaw({
|
|
@@ -918,6 +918,7 @@ const subMerge = (args, out, gid, extra, idPrefix, first) => {
|
|
|
918
918
|
};
|
|
919
919
|
const addTSAndExtra = {
|
|
920
920
|
...mapExact0(e, to),
|
|
921
|
+
...(out.whenNotMatched === 'insert' ? { deletedAt: ['deletedAt', to(nil)] } : {}),
|
|
921
922
|
touchedAt: ['touchedAt', to(current)],
|
|
922
923
|
};
|
|
923
924
|
const updater = set()(addTSAndExtra);
|
|
@@ -1280,7 +1281,8 @@ const createIndexWithRetry = async (collection, indexSpec, options) => {
|
|
|
1280
1281
|
}
|
|
1281
1282
|
catch (e) {
|
|
1282
1283
|
if ([85, 276].includes(e.code)) {
|
|
1283
|
-
log('Index created with different name', e.code, {
|
|
1284
|
+
log('Index created with different name', e.code, {
|
|
1285
|
+
collection: collection.collectionName});
|
|
1284
1286
|
break;
|
|
1285
1287
|
}
|
|
1286
1288
|
if (e.code == 12587) {
|
|
@@ -1295,6 +1297,11 @@ const createIndexWithRetry = async (collection, indexSpec, options) => {
|
|
|
1295
1297
|
break;
|
|
1296
1298
|
}
|
|
1297
1299
|
};
|
|
1300
|
+
const ensureCollection = async (db, collectionName) => {
|
|
1301
|
+
if (!(await db.listCollections({ name: collectionName }, { nameOnly: true }).next())) {
|
|
1302
|
+
await db.createCollection(collectionName);
|
|
1303
|
+
}
|
|
1304
|
+
};
|
|
1298
1305
|
|
|
1299
1306
|
const patch = ({ ...x }, k, v) => {
|
|
1300
1307
|
delete x[k];
|
|
@@ -1600,7 +1607,6 @@ const addTeardown = (it, tr) => {
|
|
|
1600
1607
|
|
|
1601
1608
|
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
|
|
1602
1609
|
const maxTimestamp = new Timestamp(0xffffffffffffffffn);
|
|
1603
|
-
const isMax = (x) => x instanceof Timestamp && x.equals(maxTimestamp);
|
|
1604
1610
|
const getCurrentTimestamp = async (db) => {
|
|
1605
1611
|
const adminDb = db.admin();
|
|
1606
1612
|
const serverStatus = await adminDb.command({ serverStatus: 1 });
|
|
@@ -1623,9 +1629,9 @@ async function waitUntilStablePast(db, oplogTs, { pollMs = 0, timeoutMs = 10_000
|
|
|
1623
1629
|
await sleep(pollMs);
|
|
1624
1630
|
}
|
|
1625
1631
|
}
|
|
1626
|
-
async function* tailOplog(db
|
|
1627
|
-
let lastTs =
|
|
1628
|
-
const reopenDelayMs =
|
|
1632
|
+
async function* tailOplog(db) {
|
|
1633
|
+
let lastTs = await getCurrentTimestamp(db);
|
|
1634
|
+
const reopenDelayMs = 250;
|
|
1629
1635
|
const coll = db.client.db('local').collection('oplog.rs');
|
|
1630
1636
|
while (true) {
|
|
1631
1637
|
const cursor = coll.find({
|
|
@@ -1639,14 +1645,12 @@ async function* tailOplog(db, opts) {
|
|
|
1639
1645
|
});
|
|
1640
1646
|
try {
|
|
1641
1647
|
for await (const doc of cursor) {
|
|
1642
|
-
lastTs = doc.ts;
|
|
1643
1648
|
if (doc.op === 'i' || '_id' in doc.o) {
|
|
1644
1649
|
const fields = new Set(Object.keys(doc.o));
|
|
1645
1650
|
fields.delete('_id');
|
|
1646
|
-
yield { fields, doc
|
|
1651
|
+
yield { fields, doc };
|
|
1647
1652
|
}
|
|
1648
1653
|
else {
|
|
1649
|
-
let changeTouched = false;
|
|
1650
1654
|
if (doc.o['$v'] !== 2) {
|
|
1651
1655
|
throw new Error(`Expected update with $v: 2, got ${JSON.stringify(doc)}`);
|
|
1652
1656
|
}
|
|
@@ -1655,21 +1659,19 @@ async function* tailOplog(db, opts) {
|
|
|
1655
1659
|
for (const updateOp in diff) {
|
|
1656
1660
|
if (['u', 'i', 'd'].includes(updateOp)) {
|
|
1657
1661
|
updatedFields.push(...Object.keys(diff[updateOp]));
|
|
1658
|
-
if (isMax(diff[updateOp]['touchedAt'])) {
|
|
1659
|
-
changeTouched = true;
|
|
1660
|
-
}
|
|
1661
1662
|
}
|
|
1662
1663
|
else if (updateOp.startsWith('s')) {
|
|
1663
1664
|
updatedFields.push(updateOp.slice(1));
|
|
1664
1665
|
}
|
|
1665
1666
|
}
|
|
1666
|
-
yield { fields: new Set(updatedFields), doc
|
|
1667
|
+
yield { fields: new Set(updatedFields), doc };
|
|
1667
1668
|
}
|
|
1668
1669
|
}
|
|
1669
1670
|
}
|
|
1670
1671
|
catch (e) {
|
|
1671
1672
|
log('oplog loop error, notifying watchers and reopening');
|
|
1672
1673
|
console.error(e);
|
|
1674
|
+
lastTs = await getCurrentTimestamp(db);
|
|
1673
1675
|
yield null;
|
|
1674
1676
|
}
|
|
1675
1677
|
finally {
|
|
@@ -1691,7 +1693,7 @@ const loop = async (db) => {
|
|
|
1691
1693
|
let notify = makePromise();
|
|
1692
1694
|
let batch = [];
|
|
1693
1695
|
const run = async () => {
|
|
1694
|
-
for await (const event of tailOplog(db
|
|
1696
|
+
for await (const event of tailOplog(db)) {
|
|
1695
1697
|
if (event?.fields.size === 0)
|
|
1696
1698
|
continue;
|
|
1697
1699
|
batch = event && batch ? [...batch, event] : null;
|
|
@@ -1718,23 +1720,6 @@ const loop = async (db) => {
|
|
|
1718
1720
|
}
|
|
1719
1721
|
continue;
|
|
1720
1722
|
}
|
|
1721
|
-
const groups = Object.groupBy(events.filter(e => e.changeTouched), ev => ev.doc.ns);
|
|
1722
|
-
for (const [ns, evs] of Object.entries(groups)) {
|
|
1723
|
-
if (!evs)
|
|
1724
|
-
continue;
|
|
1725
|
-
const [dbName, collName] = ns.split('.');
|
|
1726
|
-
if (dbName !== db.databaseName)
|
|
1727
|
-
continue;
|
|
1728
|
-
const coll = db.collection(collName);
|
|
1729
|
-
coll
|
|
1730
|
-
.bulkWrite(evs.map((e) => ({
|
|
1731
|
-
updateOne: {
|
|
1732
|
-
filter: { _id: e.doc.o['_id'] ?? e.doc.o2?._id },
|
|
1733
|
-
update: { $set: { touchedAt: e.doc.ts } },
|
|
1734
|
-
},
|
|
1735
|
-
})))
|
|
1736
|
-
.catch(() => { });
|
|
1737
|
-
}
|
|
1738
1723
|
for (const { fields, doc } of events) {
|
|
1739
1724
|
const m = watchers.get(doc.ns);
|
|
1740
1725
|
if (!m)
|
|
@@ -1802,6 +1787,7 @@ const actions = {
|
|
|
1802
1787
|
],
|
|
1803
1788
|
};
|
|
1804
1789
|
|
|
1790
|
+
const previous = (ts) => new Timestamp({ t: ts.high - 60, i: 0 });
|
|
1805
1791
|
const getFirstStages = (view, needs) => {
|
|
1806
1792
|
const { projection, hardMatch: pre, match } = view;
|
|
1807
1793
|
const projectInput = projection && $project_(spread(projection, {
|
|
@@ -1812,7 +1798,7 @@ const getFirstStages = (view, needs) => {
|
|
|
1812
1798
|
const hardMatch = removeNotYetSynchronizedFields ? $and(pre, ...removeNotYetSynchronizedFields) : pre;
|
|
1813
1799
|
const firstStages = (lastTS, keepNulls = false) => {
|
|
1814
1800
|
const hardQuery = $and(lastTS
|
|
1815
|
-
? root().of('touchedAt').has($gtTs(lastTS.ts))
|
|
1801
|
+
? root().of('touchedAt').has($gtTs(previous(lastTS.ts)))
|
|
1816
1802
|
: root().of('deletedAt').has($eq(null)), lastTS ? null : match && $expr(match), keepNulls ? pre : hardMatch);
|
|
1817
1803
|
const ln = link()
|
|
1818
1804
|
.with($match_(hardQuery));
|
|
@@ -1967,6 +1953,7 @@ const executes$2 = (view, input, streamName, skip = false, after, needs = {}) =>
|
|
|
1967
1953
|
partialFilterExpression: { updated: true, after: null, before: null },
|
|
1968
1954
|
name: 'updated_nulls_' + new UUID().toString('base64'),
|
|
1969
1955
|
});
|
|
1956
|
+
await ensureCollection(db, coll);
|
|
1970
1957
|
await db.command({
|
|
1971
1958
|
collMod: coll,
|
|
1972
1959
|
changeStreamPreAndPostImages: { enabled: true },
|
|
@@ -2118,7 +2105,7 @@ const executes$1 = (view, input, streamName, needs) => {
|
|
|
2118
2105
|
}));
|
|
2119
2106
|
const notDeleted = root().of('deletedAt').has($eq(null));
|
|
2120
2107
|
const stages = (lastTS) => {
|
|
2121
|
-
const hardQuery = $and(lastTS && root().of('touchedAt').has($gteTs(lastTS.ts)), hardMatch, notDeleted, match && $expr(match));
|
|
2108
|
+
const hardQuery = $and(lastTS && root().of('touchedAt').has($gteTs(previous(lastTS.ts))), hardMatch, notDeleted, match && $expr(match));
|
|
2122
2109
|
const ln = link().with($match_(hardQuery));
|
|
2123
2110
|
return (projectInput ? ln.with(projectInput) : ln).with(input);
|
|
2124
2111
|
};
|
|
@@ -2148,6 +2135,7 @@ const executes$1 = (view, input, streamName, needs) => {
|
|
|
2148
2135
|
const stop = withStop(step0);
|
|
2149
2136
|
const step1 = async () => {
|
|
2150
2137
|
log('creating indexes');
|
|
2138
|
+
await ensureCollection(db, coll);
|
|
2151
2139
|
await db.command({
|
|
2152
2140
|
collMod: coll,
|
|
2153
2141
|
changeStreamPreAndPostImages: { enabled: true },
|
package/index.js
CHANGED
|
@@ -84,7 +84,7 @@ const val = (val) => asExpr({
|
|
|
84
84
|
: val),
|
|
85
85
|
});
|
|
86
86
|
const current = asExpr({
|
|
87
|
-
raw: () => asExprRaw(
|
|
87
|
+
raw: () => asExprRaw('$$CLUSTER_TIME'),
|
|
88
88
|
});
|
|
89
89
|
const $let = (vars, inExpr) => asExpr({
|
|
90
90
|
raw: f => asExprRaw({
|
|
@@ -920,6 +920,7 @@ const subMerge = (args, out, gid, extra, idPrefix, first) => {
|
|
|
920
920
|
};
|
|
921
921
|
const addTSAndExtra = {
|
|
922
922
|
...mapExact0(e, to),
|
|
923
|
+
...(out.whenNotMatched === 'insert' ? { deletedAt: ['deletedAt', to(nil)] } : {}),
|
|
923
924
|
touchedAt: ['touchedAt', to(current)],
|
|
924
925
|
};
|
|
925
926
|
const updater = set()(addTSAndExtra);
|
|
@@ -1282,7 +1283,8 @@ const createIndexWithRetry = async (collection, indexSpec, options) => {
|
|
|
1282
1283
|
}
|
|
1283
1284
|
catch (e) {
|
|
1284
1285
|
if ([85, 276].includes(e.code)) {
|
|
1285
|
-
log('Index created with different name', e.code, {
|
|
1286
|
+
log('Index created with different name', e.code, {
|
|
1287
|
+
collection: collection.collectionName});
|
|
1286
1288
|
break;
|
|
1287
1289
|
}
|
|
1288
1290
|
if (e.code == 12587) {
|
|
@@ -1297,6 +1299,11 @@ const createIndexWithRetry = async (collection, indexSpec, options) => {
|
|
|
1297
1299
|
break;
|
|
1298
1300
|
}
|
|
1299
1301
|
};
|
|
1302
|
+
const ensureCollection = async (db, collectionName) => {
|
|
1303
|
+
if (!(await db.listCollections({ name: collectionName }, { nameOnly: true }).next())) {
|
|
1304
|
+
await db.createCollection(collectionName);
|
|
1305
|
+
}
|
|
1306
|
+
};
|
|
1300
1307
|
|
|
1301
1308
|
const patch = ({ ...x }, k, v) => {
|
|
1302
1309
|
delete x[k];
|
|
@@ -1602,7 +1609,6 @@ const addTeardown = (it, tr) => {
|
|
|
1602
1609
|
|
|
1603
1610
|
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
|
|
1604
1611
|
const maxTimestamp = new mongodb.Timestamp(0xffffffffffffffffn);
|
|
1605
|
-
const isMax = (x) => x instanceof mongodb.Timestamp && x.equals(maxTimestamp);
|
|
1606
1612
|
const getCurrentTimestamp = async (db) => {
|
|
1607
1613
|
const adminDb = db.admin();
|
|
1608
1614
|
const serverStatus = await adminDb.command({ serverStatus: 1 });
|
|
@@ -1625,9 +1631,9 @@ async function waitUntilStablePast(db, oplogTs, { pollMs = 0, timeoutMs = 10_000
|
|
|
1625
1631
|
await sleep(pollMs);
|
|
1626
1632
|
}
|
|
1627
1633
|
}
|
|
1628
|
-
async function* tailOplog(db
|
|
1629
|
-
let lastTs =
|
|
1630
|
-
const reopenDelayMs =
|
|
1634
|
+
async function* tailOplog(db) {
|
|
1635
|
+
let lastTs = await getCurrentTimestamp(db);
|
|
1636
|
+
const reopenDelayMs = 250;
|
|
1631
1637
|
const coll = db.client.db('local').collection('oplog.rs');
|
|
1632
1638
|
while (true) {
|
|
1633
1639
|
const cursor = coll.find({
|
|
@@ -1641,14 +1647,12 @@ async function* tailOplog(db, opts) {
|
|
|
1641
1647
|
});
|
|
1642
1648
|
try {
|
|
1643
1649
|
for await (const doc of cursor) {
|
|
1644
|
-
lastTs = doc.ts;
|
|
1645
1650
|
if (doc.op === 'i' || '_id' in doc.o) {
|
|
1646
1651
|
const fields = new Set(Object.keys(doc.o));
|
|
1647
1652
|
fields.delete('_id');
|
|
1648
|
-
yield { fields, doc
|
|
1653
|
+
yield { fields, doc };
|
|
1649
1654
|
}
|
|
1650
1655
|
else {
|
|
1651
|
-
let changeTouched = false;
|
|
1652
1656
|
if (doc.o['$v'] !== 2) {
|
|
1653
1657
|
throw new Error(`Expected update with $v: 2, got ${JSON.stringify(doc)}`);
|
|
1654
1658
|
}
|
|
@@ -1657,21 +1661,19 @@ async function* tailOplog(db, opts) {
|
|
|
1657
1661
|
for (const updateOp in diff) {
|
|
1658
1662
|
if (['u', 'i', 'd'].includes(updateOp)) {
|
|
1659
1663
|
updatedFields.push(...Object.keys(diff[updateOp]));
|
|
1660
|
-
if (isMax(diff[updateOp]['touchedAt'])) {
|
|
1661
|
-
changeTouched = true;
|
|
1662
|
-
}
|
|
1663
1664
|
}
|
|
1664
1665
|
else if (updateOp.startsWith('s')) {
|
|
1665
1666
|
updatedFields.push(updateOp.slice(1));
|
|
1666
1667
|
}
|
|
1667
1668
|
}
|
|
1668
|
-
yield { fields: new Set(updatedFields), doc
|
|
1669
|
+
yield { fields: new Set(updatedFields), doc };
|
|
1669
1670
|
}
|
|
1670
1671
|
}
|
|
1671
1672
|
}
|
|
1672
1673
|
catch (e) {
|
|
1673
1674
|
log('oplog loop error, notifying watchers and reopening');
|
|
1674
1675
|
console.error(e);
|
|
1676
|
+
lastTs = await getCurrentTimestamp(db);
|
|
1675
1677
|
yield null;
|
|
1676
1678
|
}
|
|
1677
1679
|
finally {
|
|
@@ -1693,7 +1695,7 @@ const loop = async (db) => {
|
|
|
1693
1695
|
let notify = makePromise();
|
|
1694
1696
|
let batch = [];
|
|
1695
1697
|
const run = async () => {
|
|
1696
|
-
for await (const event of tailOplog(db
|
|
1698
|
+
for await (const event of tailOplog(db)) {
|
|
1697
1699
|
if (event?.fields.size === 0)
|
|
1698
1700
|
continue;
|
|
1699
1701
|
batch = event && batch ? [...batch, event] : null;
|
|
@@ -1720,23 +1722,6 @@ const loop = async (db) => {
|
|
|
1720
1722
|
}
|
|
1721
1723
|
continue;
|
|
1722
1724
|
}
|
|
1723
|
-
const groups = Object.groupBy(events.filter(e => e.changeTouched), ev => ev.doc.ns);
|
|
1724
|
-
for (const [ns, evs] of Object.entries(groups)) {
|
|
1725
|
-
if (!evs)
|
|
1726
|
-
continue;
|
|
1727
|
-
const [dbName, collName] = ns.split('.');
|
|
1728
|
-
if (dbName !== db.databaseName)
|
|
1729
|
-
continue;
|
|
1730
|
-
const coll = db.collection(collName);
|
|
1731
|
-
coll
|
|
1732
|
-
.bulkWrite(evs.map((e) => ({
|
|
1733
|
-
updateOne: {
|
|
1734
|
-
filter: { _id: e.doc.o['_id'] ?? e.doc.o2?._id },
|
|
1735
|
-
update: { $set: { touchedAt: e.doc.ts } },
|
|
1736
|
-
},
|
|
1737
|
-
})))
|
|
1738
|
-
.catch(() => { });
|
|
1739
|
-
}
|
|
1740
1725
|
for (const { fields, doc } of events) {
|
|
1741
1726
|
const m = watchers.get(doc.ns);
|
|
1742
1727
|
if (!m)
|
|
@@ -1804,6 +1789,7 @@ const actions = {
|
|
|
1804
1789
|
],
|
|
1805
1790
|
};
|
|
1806
1791
|
|
|
1792
|
+
const previous = (ts) => new mongodb.Timestamp({ t: ts.high - 60, i: 0 });
|
|
1807
1793
|
const getFirstStages = (view, needs) => {
|
|
1808
1794
|
const { projection, hardMatch: pre, match } = view;
|
|
1809
1795
|
const projectInput = projection && $project_(spread(projection, {
|
|
@@ -1814,7 +1800,7 @@ const getFirstStages = (view, needs) => {
|
|
|
1814
1800
|
const hardMatch = removeNotYetSynchronizedFields ? $and(pre, ...removeNotYetSynchronizedFields) : pre;
|
|
1815
1801
|
const firstStages = (lastTS, keepNulls = false) => {
|
|
1816
1802
|
const hardQuery = $and(lastTS
|
|
1817
|
-
? root().of('touchedAt').has($gtTs(lastTS.ts))
|
|
1803
|
+
? root().of('touchedAt').has($gtTs(previous(lastTS.ts)))
|
|
1818
1804
|
: root().of('deletedAt').has($eq(null)), lastTS ? null : match && $expr(match), keepNulls ? pre : hardMatch);
|
|
1819
1805
|
const ln = link()
|
|
1820
1806
|
.with($match_(hardQuery));
|
|
@@ -1969,6 +1955,7 @@ const executes$2 = (view, input, streamName, skip = false, after, needs = {}) =>
|
|
|
1969
1955
|
partialFilterExpression: { updated: true, after: null, before: null },
|
|
1970
1956
|
name: 'updated_nulls_' + new mongodb.UUID().toString('base64'),
|
|
1971
1957
|
});
|
|
1958
|
+
await ensureCollection(db, coll);
|
|
1972
1959
|
await db.command({
|
|
1973
1960
|
collMod: coll,
|
|
1974
1961
|
changeStreamPreAndPostImages: { enabled: true },
|
|
@@ -2120,7 +2107,7 @@ const executes$1 = (view, input, streamName, needs) => {
|
|
|
2120
2107
|
}));
|
|
2121
2108
|
const notDeleted = root().of('deletedAt').has($eq(null));
|
|
2122
2109
|
const stages = (lastTS) => {
|
|
2123
|
-
const hardQuery = $and(lastTS && root().of('touchedAt').has($gteTs(lastTS.ts)), hardMatch, notDeleted, match && $expr(match));
|
|
2110
|
+
const hardQuery = $and(lastTS && root().of('touchedAt').has($gteTs(previous(lastTS.ts))), hardMatch, notDeleted, match && $expr(match));
|
|
2124
2111
|
const ln = link().with($match_(hardQuery));
|
|
2125
2112
|
return (projectInput ? ln.with(projectInput) : ln).with(input);
|
|
2126
2113
|
};
|
|
@@ -2150,6 +2137,7 @@ const executes$1 = (view, input, streamName, needs) => {
|
|
|
2150
2137
|
const stop = withStop(step0);
|
|
2151
2138
|
const step1 = async () => {
|
|
2152
2139
|
log('creating indexes');
|
|
2140
|
+
await ensureCollection(db, coll);
|
|
2153
2141
|
await db.command({
|
|
2154
2142
|
collMod: coll,
|
|
2155
2143
|
changeStreamPreAndPostImages: { enabled: true },
|