@resolveio/server-lib 20.12.62 → 20.12.64

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.
@@ -51,7 +51,7 @@ export declare class SubscriptionManager {
51
51
  static create(wss: WebSocket.Server, serverConfig: any, monitorManagerFunction: MonitorManagerFunction): SubscriptionManager;
52
52
  private initialize;
53
53
  private setCacheLimit;
54
- invalidatePubsCache(collection: string, type: string, documentId?: any): Promise<void>;
54
+ invalidatePubsCache(collection: string, type: string, documentId?: any, document?: any): Promise<void>;
55
55
  private _executeInvalidation;
56
56
  private subscriptionReferencesCollection;
57
57
  private delay;
@@ -84,6 +84,9 @@ export declare class SubscriptionManager {
84
84
  private updateSubscriptionDependencies;
85
85
  private normalizeDocumentId;
86
86
  private getDocumentIdQueryCandidates;
87
+ private isFilterSafeForInMemory;
88
+ private documentIdMatchesEvent;
89
+ private resolveFilterMatch;
87
90
  private documentMatchesFilter;
88
91
  private shouldInvalidateSubscription;
89
92
  private shouldInvalidateSubscriptionForEvents;
@@ -74,7 +74,6 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
74
74
  Object.defineProperty(exports, "__esModule", { value: true });
75
75
  exports.SubscriptionManager = void 0;
76
76
  var NodeCache = require("node-cache");
77
- var os_1 = require("os");
78
77
  var flag_collection_1 = require("../collections/flag.collection");
79
78
  var logged_in_users_collection_1 = require("../collections/logged-in-users.collection");
80
79
  var app_status_1 = require("../publications/app-status");
@@ -95,8 +94,18 @@ var common_1 = require("../util/common");
95
94
  var error_reporter_1 = require("../util/error-reporter");
96
95
  var error_tracking_1 = require("../util/error-tracking");
97
96
  var subscription_dependency_context_1 = require("../util/subscription-dependency-context");
98
- var numCPUs = (0, os_1.cpus)().length;
99
97
  var v8 = require('v8');
98
+ var sift = require('sift');
99
+ function resolveHeapLimitBytes() {
100
+ var stats = v8.getHeapStatistics();
101
+ if (stats && typeof stats.heap_size_limit === 'number') {
102
+ return stats.heap_size_limit;
103
+ }
104
+ if (stats && typeof stats.total_available_size === 'number') {
105
+ return stats.total_available_size;
106
+ }
107
+ return 0;
108
+ }
100
109
  // interface CurrentPerformanceMonitor {
101
110
  // _id: number;
102
111
  // function: string;
@@ -116,7 +125,7 @@ var SubscriptionManager = /** @class */ (function () {
116
125
  this._mongoQueue = [];
117
126
  this._mongoQueueId = 0;
118
127
  this._cacheId = 1;
119
- this._heapSize = v8.getHeapStatistics() / numCPUs;
128
+ this._heapSize = resolveHeapLimitBytes();
120
129
  this._enableDebug = false;
121
130
  this._enableDependencyDebug = false;
122
131
  this._debugOplogCollections = [];
@@ -352,7 +361,7 @@ var SubscriptionManager = /** @class */ (function () {
352
361
  this._heapLimit = this._heapSize * 0.3; // Use 50% of total heap size
353
362
  }
354
363
  };
355
- SubscriptionManager.prototype.invalidatePubsCache = function (collection, type, documentId) {
364
+ SubscriptionManager.prototype.invalidatePubsCache = function (collection, type, documentId, document) {
356
365
  return __awaiter(this, void 0, void 0, function () {
357
366
  var queue, debounceKey, now, firstInvalidationTime, waitedTooLong;
358
367
  var _this = this;
@@ -364,7 +373,7 @@ var SubscriptionManager = /** @class */ (function () {
364
373
  queue = { events: [] };
365
374
  this._pendingInvalidations.set(collection, queue);
366
375
  }
367
- queue.events.push({ type: type, documentId: documentId });
376
+ queue.events.push({ type: type, documentId: documentId, document: document });
368
377
  debounceKey = collection;
369
378
  now = Date.now();
370
379
  firstInvalidationTime = this._invalidationPendingTimestamps.get(debounceKey) || now;
@@ -1239,7 +1248,7 @@ var SubscriptionManager = /** @class */ (function () {
1239
1248
  _a.label = 4;
1240
1249
  case 4:
1241
1250
  _a.trys.push([4, 5, , 9]);
1242
- this._oplog$ = resolveio_server_app_1.ResolveIOServer.getMongoConnection().watch(pipeline, { resumeAfter: resumeToken });
1251
+ this._oplog$ = resolveio_server_app_1.ResolveIOServer.getMongoConnection().watch(pipeline, { resumeAfter: resumeToken, fullDocument: 'updateLookup' });
1243
1252
  startedWithResumeToken = true;
1244
1253
  return [3 /*break*/, 9];
1245
1254
  case 5:
@@ -1256,18 +1265,18 @@ var SubscriptionManager = /** @class */ (function () {
1256
1265
  _a.sent();
1257
1266
  lastResumeToken_1 = null;
1258
1267
  console.log(new Date(), 'oplog resumeAfter failed, starting fresh', error_6);
1259
- this._oplog$ = resolveio_server_app_1.ResolveIOServer.getMongoConnection().watch(pipeline);
1268
+ this._oplog$ = resolveio_server_app_1.ResolveIOServer.getMongoConnection().watch(pipeline, { fullDocument: 'updateLookup' });
1260
1269
  startedWithResumeToken = false;
1261
1270
  this.queueFullResync('oplog-resumeAfter-failed');
1262
1271
  return [3 /*break*/, 9];
1263
1272
  case 9: return [3 /*break*/, 11];
1264
1273
  case 10:
1265
- this._oplog$ = resolveio_server_app_1.ResolveIOServer.getMongoConnection().watch(pipeline);
1274
+ this._oplog$ = resolveio_server_app_1.ResolveIOServer.getMongoConnection().watch(pipeline, { fullDocument: 'updateLookup' });
1266
1275
  _a.label = 11;
1267
1276
  case 11:
1268
1277
  console.log(new Date(), 'oplog started', startedWithResumeToken ? '(resumeAfter)' : '');
1269
1278
  this._oplog$.on('change', function (doc) { return __awaiter(_this, void 0, void 0, function () {
1270
- var collection, docId, flag, dependencyFlag;
1279
+ var collection, fullDocument, docId, flag, dependencyFlag;
1271
1280
  return __generator(this, function (_a) {
1272
1281
  switch (_a.label) {
1273
1282
  case 0:
@@ -1288,18 +1297,19 @@ var SubscriptionManager = /** @class */ (function () {
1288
1297
  }
1289
1298
  if (!collection) return [3 /*break*/, 10];
1290
1299
  this._debugOplogHits += 1;
1291
- docId = doc.documentKey && doc.documentKey['_id'] !== undefined ? doc.documentKey['_id'] : (doc['fullDocument'] && doc['fullDocument']['_id'] !== undefined ? doc['fullDocument']['_id'] : null);
1300
+ fullDocument = doc['fullDocument'];
1301
+ docId = doc.documentKey && doc.documentKey['_id'] !== undefined ? doc.documentKey['_id'] : (fullDocument && fullDocument['_id'] !== undefined ? fullDocument['_id'] : null);
1292
1302
  if (!(doc.operationType === 'insert')) return [3 /*break*/, 5];
1293
1303
  if (!(collection === 'support-tickets')) return [3 /*break*/, 2];
1294
1304
  if (!(this.serverConfig['ROOT_URL'] === 'https://resolveio.com')) return [3 /*break*/, 2];
1295
1305
  resolveio_server_app_1.ResolveIOServer.getMainServer().getMethodManager().callMethod.call(resolveio_server_app_1.ResolveIOServer.getMainServer().getMethodManager(), 'sendSupportTicketEmail', doc.documentKey['_id']);
1296
- return [4 /*yield*/, this.invalidatePubsCache(collection, 'insert', docId)];
1306
+ return [4 /*yield*/, this.invalidatePubsCache(collection, 'insert', docId, fullDocument)];
1297
1307
  case 1:
1298
1308
  _a.sent();
1299
1309
  _a.label = 2;
1300
1310
  case 2:
1301
1311
  if (!(collection !== 'method-responses')) return [3 /*break*/, 4];
1302
- return [4 /*yield*/, this.invalidatePubsCache(collection, 'insert', docId)];
1312
+ return [4 /*yield*/, this.invalidatePubsCache(collection, 'insert', docId, fullDocument)];
1303
1313
  case 3:
1304
1314
  _a.sent();
1305
1315
  _a.label = 4;
@@ -1307,7 +1317,7 @@ var SubscriptionManager = /** @class */ (function () {
1307
1317
  case 5:
1308
1318
  if (!(doc.operationType === 'update' || doc.operationType === 'replace')) return [3 /*break*/, 8];
1309
1319
  if (!(collection !== 'method-responses')) return [3 /*break*/, 7];
1310
- return [4 /*yield*/, this.invalidatePubsCache(collection, 'update', docId)];
1320
+ return [4 /*yield*/, this.invalidatePubsCache(collection, 'update', docId, fullDocument)];
1311
1321
  case 6:
1312
1322
  _a.sent();
1313
1323
  _a.label = 7;
@@ -1839,14 +1849,143 @@ var SubscriptionManager = /** @class */ (function () {
1839
1849
  }
1840
1850
  return candidates;
1841
1851
  };
1842
- SubscriptionManager.prototype.documentMatchesFilter = function (collection, documentId, filter) {
1852
+ SubscriptionManager.prototype.isFilterSafeForInMemory = function (filter) {
1853
+ if (!filter || typeof filter !== 'object') {
1854
+ return false;
1855
+ }
1856
+ var allowedOperators = new Set([
1857
+ '$and',
1858
+ '$or',
1859
+ '$nor',
1860
+ '$in',
1861
+ '$nin',
1862
+ '$eq',
1863
+ '$ne',
1864
+ '$gt',
1865
+ '$gte',
1866
+ '$lt',
1867
+ '$lte',
1868
+ '$exists',
1869
+ '$size',
1870
+ '$all',
1871
+ '$elemMatch',
1872
+ '$regex',
1873
+ '$options'
1874
+ ]);
1875
+ var disallowedOperators = new Set([
1876
+ '$where',
1877
+ '$expr',
1878
+ '$text',
1879
+ '$geoWithin',
1880
+ '$geoIntersects',
1881
+ '$near',
1882
+ '$nearSphere',
1883
+ '$jsonSchema',
1884
+ '$mod',
1885
+ '$type',
1886
+ '$bitsAllSet',
1887
+ '$bitsAllClear',
1888
+ '$bitsAnySet',
1889
+ '$bitsAnyClear'
1890
+ ]);
1891
+ var walk = function (value) {
1892
+ var e_8, _a;
1893
+ if (Array.isArray(value)) {
1894
+ return value.every(function (item) { return walk(item); });
1895
+ }
1896
+ if (!value || typeof value !== 'object') {
1897
+ return true;
1898
+ }
1899
+ try {
1900
+ for (var _b = __values(Object.entries(value)), _c = _b.next(); !_c.done; _c = _b.next()) {
1901
+ var _d = __read(_c.value, 2), key = _d[0], entry = _d[1];
1902
+ if (key.startsWith('$')) {
1903
+ if (disallowedOperators.has(key)) {
1904
+ return false;
1905
+ }
1906
+ if (!allowedOperators.has(key)) {
1907
+ return false;
1908
+ }
1909
+ }
1910
+ if (!walk(entry)) {
1911
+ return false;
1912
+ }
1913
+ }
1914
+ }
1915
+ catch (e_8_1) { e_8 = { error: e_8_1 }; }
1916
+ finally {
1917
+ try {
1918
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
1919
+ }
1920
+ finally { if (e_8) throw e_8.error; }
1921
+ }
1922
+ return true;
1923
+ };
1924
+ return walk(filter);
1925
+ };
1926
+ SubscriptionManager.prototype.documentIdMatchesEvent = function (documentId, document) {
1927
+ var e_9, _a;
1928
+ var _this = this;
1929
+ if (!document || documentId === undefined || documentId === null) {
1930
+ return true;
1931
+ }
1932
+ var eventCandidates = this.getDocumentIdQueryCandidates(documentId)
1933
+ .map(function (id) { return _this.normalizeDocumentId(id); })
1934
+ .filter(Boolean);
1935
+ var docCandidates = this.getDocumentIdQueryCandidates(document === null || document === void 0 ? void 0 : document._id)
1936
+ .map(function (id) { return _this.normalizeDocumentId(id); })
1937
+ .filter(Boolean);
1938
+ if (!eventCandidates.length || !docCandidates.length) {
1939
+ return true;
1940
+ }
1941
+ var docSet = new Set(docCandidates);
1942
+ try {
1943
+ for (var eventCandidates_1 = __values(eventCandidates), eventCandidates_1_1 = eventCandidates_1.next(); !eventCandidates_1_1.done; eventCandidates_1_1 = eventCandidates_1.next()) {
1944
+ var candidate = eventCandidates_1_1.value;
1945
+ if (docSet.has(candidate)) {
1946
+ return true;
1947
+ }
1948
+ }
1949
+ }
1950
+ catch (e_9_1) { e_9 = { error: e_9_1 }; }
1951
+ finally {
1952
+ try {
1953
+ if (eventCandidates_1_1 && !eventCandidates_1_1.done && (_a = eventCandidates_1.return)) _a.call(eventCandidates_1);
1954
+ }
1955
+ finally { if (e_9) throw e_9.error; }
1956
+ }
1957
+ return false;
1958
+ };
1959
+ SubscriptionManager.prototype.resolveFilterMatch = function (filter, document, documentId) {
1960
+ if (!document || !filter || typeof filter !== 'object') {
1961
+ return null;
1962
+ }
1963
+ if (!this.isFilterSafeForInMemory(filter)) {
1964
+ return null;
1965
+ }
1966
+ if (!this.documentIdMatchesEvent(documentId, document)) {
1967
+ return false;
1968
+ }
1969
+ try {
1970
+ var matcher = sift(filter);
1971
+ return matcher(document);
1972
+ }
1973
+ catch (_a) {
1974
+ return null;
1975
+ }
1976
+ };
1977
+ SubscriptionManager.prototype.documentMatchesFilter = function (collection, documentId, filter, document) {
1843
1978
  return __awaiter(this, void 0, void 0, function () {
1844
- var db, filterCopy, idCandidates, idCandidates_1, idCandidates_1_1, idValue, combinedFilter, doc, e_8_1, _a;
1845
- var e_8, _b;
1979
+ var inMemoryMatch, db, filterCopy, idCandidates, idCandidates_1, idCandidates_1_1, idValue, combinedFilter, doc, e_10_1, _a;
1980
+ var e_10, _b;
1846
1981
  return __generator(this, function (_c) {
1847
1982
  switch (_c.label) {
1848
1983
  case 0:
1849
1984
  _c.trys.push([0, 9, , 10]);
1985
+ inMemoryMatch = this.resolveFilterMatch(filter, document, documentId);
1986
+ if (inMemoryMatch !== null) {
1987
+ return [2 /*return*/, inMemoryMatch];
1988
+ }
1850
1989
  db = resolveio_server_app_1.ResolveIOServer.getMainDB();
1851
1990
  filterCopy = (0, common_1.deepCopy)(filter) || {};
1852
1991
  idCandidates = this.getDocumentIdQueryCandidates(documentId);
@@ -1876,14 +2015,14 @@ var SubscriptionManager = /** @class */ (function () {
1876
2015
  return [3 /*break*/, 2];
1877
2016
  case 5: return [3 /*break*/, 8];
1878
2017
  case 6:
1879
- e_8_1 = _c.sent();
1880
- e_8 = { error: e_8_1 };
2018
+ e_10_1 = _c.sent();
2019
+ e_10 = { error: e_10_1 };
1881
2020
  return [3 /*break*/, 8];
1882
2021
  case 7:
1883
2022
  try {
1884
2023
  if (idCandidates_1_1 && !idCandidates_1_1.done && (_b = idCandidates_1.return)) _b.call(idCandidates_1);
1885
2024
  }
1886
- finally { if (e_8) throw e_8.error; }
2025
+ finally { if (e_10) throw e_10.error; }
1887
2026
  return [7 /*endfinally*/];
1888
2027
  case 8: return [2 /*return*/, false];
1889
2028
  case 9:
@@ -1894,10 +2033,10 @@ var SubscriptionManager = /** @class */ (function () {
1894
2033
  });
1895
2034
  });
1896
2035
  };
1897
- SubscriptionManager.prototype.shouldInvalidateSubscription = function (sub, collection, type, documentId) {
2036
+ SubscriptionManager.prototype.shouldInvalidateSubscription = function (sub, collection, type, documentId, document) {
1898
2037
  return __awaiter(this, void 0, void 0, function () {
1899
- var normalizedDocumentId, hasDependencyData, trackedIds, filters, filters_1, filters_1_1, filter, e_9_1;
1900
- var e_9, _a;
2038
+ var normalizedDocumentId, hasDependencyData, trackedIds, filters, filters_1, filters_1_1, filter, e_11_1;
2039
+ var e_11, _a;
1901
2040
  var _b, _c;
1902
2041
  return __generator(this, function (_d) {
1903
2042
  switch (_d.label) {
@@ -1942,7 +2081,7 @@ var SubscriptionManager = /** @class */ (function () {
1942
2081
  case 2:
1943
2082
  if (!!filters_1_1.done) return [3 /*break*/, 5];
1944
2083
  filter = filters_1_1.value;
1945
- return [4 /*yield*/, this.documentMatchesFilter(collection, documentId, filter)];
2084
+ return [4 /*yield*/, this.documentMatchesFilter(collection, documentId, filter, document)];
1946
2085
  case 3:
1947
2086
  if (_d.sent()) {
1948
2087
  this.dependencyDebug('Invalidate due to filter match', { publication: sub.publication, collection: collection, type: type, documentId: normalizedDocumentId, filter: filter });
@@ -1954,14 +2093,14 @@ var SubscriptionManager = /** @class */ (function () {
1954
2093
  return [3 /*break*/, 2];
1955
2094
  case 5: return [3 /*break*/, 8];
1956
2095
  case 6:
1957
- e_9_1 = _d.sent();
1958
- e_9 = { error: e_9_1 };
2096
+ e_11_1 = _d.sent();
2097
+ e_11 = { error: e_11_1 };
1959
2098
  return [3 /*break*/, 8];
1960
2099
  case 7:
1961
2100
  try {
1962
2101
  if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1);
1963
2102
  }
1964
- finally { if (e_9) throw e_9.error; }
2103
+ finally { if (e_11) throw e_11.error; }
1965
2104
  return [7 /*endfinally*/];
1966
2105
  case 8:
1967
2106
  this.dependencyDebug('Skip invalidation after dependency checks', { publication: sub.publication, collection: collection, type: type, documentId: normalizedDocumentId });
@@ -1972,8 +2111,8 @@ var SubscriptionManager = /** @class */ (function () {
1972
2111
  };
1973
2112
  SubscriptionManager.prototype.shouldInvalidateSubscriptionForEvents = function (sub, collection, events) {
1974
2113
  return __awaiter(this, void 0, void 0, function () {
1975
- var sawInsert, sawDelete, events_1, events_1_1, event_1, e_10_1, paginationMeta, paginationReasons;
1976
- var e_10, _a;
2114
+ var sawInsert, sawDelete, events_1, events_1_1, event_1, e_12_1, paginationMeta, paginationReasons;
2115
+ var e_12, _a;
1977
2116
  return __generator(this, function (_b) {
1978
2117
  switch (_b.label) {
1979
2118
  case 0:
@@ -1993,7 +2132,7 @@ var SubscriptionManager = /** @class */ (function () {
1993
2132
  else if (event_1.type === 'delete') {
1994
2133
  sawDelete = true;
1995
2134
  }
1996
- return [4 /*yield*/, this.shouldInvalidateSubscription(sub, collection, event_1.type, event_1.documentId)];
2135
+ return [4 /*yield*/, this.shouldInvalidateSubscription(sub, collection, event_1.type, event_1.documentId, event_1.document)];
1997
2136
  case 3:
1998
2137
  if (_b.sent()) {
1999
2138
  return [2 /*return*/, true];
@@ -2004,14 +2143,14 @@ var SubscriptionManager = /** @class */ (function () {
2004
2143
  return [3 /*break*/, 2];
2005
2144
  case 5: return [3 /*break*/, 8];
2006
2145
  case 6:
2007
- e_10_1 = _b.sent();
2008
- e_10 = { error: e_10_1 };
2146
+ e_12_1 = _b.sent();
2147
+ e_12 = { error: e_12_1 };
2009
2148
  return [3 /*break*/, 8];
2010
2149
  case 7:
2011
2150
  try {
2012
2151
  if (events_1_1 && !events_1_1.done && (_a = events_1.return)) _a.call(events_1);
2013
2152
  }
2014
- finally { if (e_10) throw e_10.error; }
2153
+ finally { if (e_12) throw e_12.error; }
2015
2154
  return [7 /*endfinally*/];
2016
2155
  case 8:
2017
2156
  paginationMeta = this.getPaginationMeta(sub, collection);