You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+4Lines changed: 4 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -856,13 +856,17 @@ Main agent singleton instance.
856
856
857
857
### v1.0.19 (Bug Fixes & Code Quality)
858
858
859
+
- **Fix LiveQueriesCollector showing 0 observers and 0 metrics** - Completely rewrote observer interception. The previous approach wrapped `Mongo.Collection.prototype.find` to patch `cursor.observe`/`cursor.observeChanges` on each returned cursor instance, but this failed because: (1) in Meteor 3.x, `observeChanges` is async (returns a Promise via `MongoConnection._observeChanges`) but the wrapper treated it synchronously; (2) the per-cursor instance patching was fragile. Now hooks directly into `MongoInternals.Connection.prototype._observeChanges` — the single async bottleneck ALL server-side observers funnel through. Uses a two-phase tracking approach: `_createObserverData()` creates a provisional observer record BEFORE calling the original `_observeChanges`, so that wrapped `added`/`changed`/`removed` callbacks can count initial documents arriving during the await. `_finalizeObserver()` then links the handle's multiplexer and driver type after the await. Deduplicates by multiplexer identity (not query hash) so observers sharing a Meteor ObserveMultiplexer are counted as one server-side resource with multiple handlers. Falls back to `Collection.prototype.find` wrapping when `MongoInternals` is unavailable.
860
+
-**Fix LiveQueriesCollector config missing from DEFAULT_CONFIG** - `collectLiveQueries`, `liveQueriesInterval`, and `liveQueriesPerformanceThresholds` were defined in the `SkySignalAgent` constructor but missing from `config.js``DEFAULT_CONFIG`. Since `mergeConfig()` spreads `DEFAULT_CONFIG` first, these values were always overwritten to `undefined`, silently disabling live query collection.
859
861
-**Fix container memory usage reporting >100%** - `SystemMetricsCollector` previously calculated container memory as `processMemory.rss / constrainedMemory * 100`, which could exceed 100% because RSS (Resident Set Size) includes shared library pages, memory-mapped files, and kernel page cache that don't count against the container's cgroup memory limit. Now uses `process.availableMemory()` (Node 19+), which reads directly from the cgroup memory controller and accounts for reclaimable buffers, to compute usage as `(constrainedMemory - availableMemory) / constrainedMemory * 100`. Falls back to `heapUsed / constrainedMemory * 100` on older Node versions. This aligns reported memory with what container orchestrators (e.g., Meteor Galaxy) actually report.
860
862
-**Fix observer stop logging crash** - `LiveQueriesCollector._wrapHandle()` used `this._log()` inside a regular `function()` callback where `this` referred to the handle object, not the collector instance. Changed to `self._log()` to use the captured closure variable. Previously, calling `handle.stop()` would throw `TypeError: this._log is not a function`, silently preventing observer lifecycle metrics from being recorded.
861
863
-**Fix P95 percentile off-by-one** - `MongoPoolCollector`, `DnsTimingCollector`, and `DiagnosticsChannelCollector` all used `Math.floor(count * 0.95)` to index into a sorted array, which overshoots the true 95th percentile by one position (e.g., for 100 items, returns the 96th element instead of the 95th). Changed to `Math.ceil(count * p) - 1` across all three collectors. Extracted to shared `percentile()` utility in `lib/utils/percentile.js`.
862
864
-**Fix MongoPoolCollector.stop() killing other event listeners** - `stop()` called `client.removeAllListeners(eventName)` for each pool event, which removed ALL listeners for that event — including those registered by the application or other collectors. Now stores individual handler references in `start()` and calls `client.removeListener(eventName, handler)` in `stop()` to remove only the collector's own handlers.
863
865
-**Fix circular buffer read after wrap-around** - `MongoPoolCollector._calculateCheckoutMetrics()` used `checkoutSamples.slice(0, count)` to extract samples, which returns incorrect data after the circular buffer wraps (old data mixed with new). Now correctly reads from the current write index forward using modular arithmetic to reconstruct the proper time-ordered sequence.
864
866
-**Shared percentile utility** - Extracted percentile calculation to `lib/utils/percentile.js` with `percentile(sorted, p)` and `percentiles(values)` functions, replacing duplicated math in `MongoPoolCollector`, `DnsTimingCollector`, and `DiagnosticsChannelCollector`.
865
867
-**Shared buffer eviction utility** - Extracted array trimming to `lib/utils/buffer.js` with `trimToMaxSize(array, maxSize)`, replacing duplicated `splice(0, length - max)` patterns in `DnsTimingCollector`, `DiagnosticsChannelCollector`, and `MongoPoolCollector._recordPoolWaitTime`.
868
+
-**Leak-detection field tests** - Added 19 unit tests (`LiveQueriesCollector.leakFields.test.js`) verifying the collector produces correct values for fields used by the server-side `ObserverLeakDetectionService`: `_wrapCallbacks` correctly increments `liveUpdateCount` and `lastActivityAt` only after initial load completes (not during the initial document fetch), `_wrapHandle` calculates `observerLifespan` in seconds on stop, and `_createObserverData` initializes all leak-relevant fields to safe defaults. These tests ensure the agent emits the data contract that leak detection heuristics (inactive observers, long-lived stale observers, orphaned observers) depend on.
869
+
-**Remove stale `_generateQuerySignature` tests** - Deleted 5 tests for `_generateQuerySignature` in `LiveQueriesCollector.test.js` that were left behind when the method was removed during the v1.0.19 observer interception rewrite. These tests were failing with `TypeError: collector._generateQuerySignature is not a function`.
0 commit comments