Commit Graph

376 Commits

Author SHA1 Message Date
Aditya Ujeniya f83f8afa86 fix(metricstore): make stats read path non-mutating to avoid data race
recomputeStats() wrote statSum/statSamples/statMin/statMax/statsValid from
inside the stats() fast path, which runs under only a shared RLock via
MemoryStore.Stats -> Level.findBuffers. Two concurrent queries on the same
invalid buffer could both enter the fast path and race on those fields.

Require statsValid in the fast-path guard instead of recomputing inline;
invalid buffers now fall through to the existing point-by-point scan, which
only reads b.data. recomputeStats() is unchanged and still used by
checkpoint loadFile at single-threaded load time.

Updated stats_test.go: overwritten buffers now stay statsValid=false after a
query (documenting non-mutating reads); TestStatsMultiBufferChain and
TestStatsFastPathThenPartialTail now recompute stats after building bare
buffers so the fast-path cache fold is still covered; added
TestStatsConcurrentQueriesNoRace (race-clean under -race for both valid and
post-overwrite buffers) and TestStatsGappedChain (real inter-buffer gap
correctly excluded from Samples/Min/Max/Avg).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 11:03:16 +02:00
Aditya Ujeniya e37b2e71a6 feat(metricstore): compute buffer stats eagerly on checkpoint load 2026-07-20 10:49:24 +02:00
Aditya Ujeniya 650db33b22 docs(metricstore): fix misleading comment in stats fast-path test 2026-07-20 10:47:33 +02:00
Aditya Ujeniya 9d5268a828 fix(metricstore): guard stats fast path against empty buffer
Add len(b.data) > 0 guard to prevent infinite loop when an empty linked
buffer is reachable. With the guard false, the normal path handles empty
buffers safely via the existing t < b.start || idx >= len(b.data) check.

Add regression test TestStatsFastPathThenPartialTail to pin the boundary:
three-buffer chain where earlier buffers are fully covered (fast path) and
the last buffer is partially covered (normal path).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 10:43:43 +02:00
Aditya Ujeniya 9eb6d0e83e feat(metricstore): serve full-buffer stats from cached aggregate
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 10:30:42 +02:00
Aditya Ujeniya b6a9f7ed3c feat(metricstore): add recomputeStats full-scan helper 2026-07-20 10:26:30 +02:00
Aditya Ujeniya 5ff376c742 style(metricstore): gofmt stats_test comment alignment 2026-07-20 10:25:16 +02:00
Aditya Ujeniya 931a2a6e79 feat(metricstore): maintain running buffer stats on write 2026-07-20 10:22:16 +02:00
Aditya Ujeniya aa61ccb88b feat(metricstore): add running-stat fields to buffer
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 10:17:12 +02:00
Aditya Ujeniya 94799aca36 fix: drop unconditional force-free of buffers retained for running jobs
MemoryUsageTracker freed all buffers older than the retention cutoff on
every tick whenever the last retention pass had excluded used nodes,
regardless of memory pressure — discarding the history kept in memory
for long-running jobs. Buffers are now only reclaimed by the existing
emergency path once memory usage exceeds the configured cap. The
now-unused selectorsExcluded state is removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 15:45:24 +02:00
Aditya Ujeniya dd2f5968b1 feat: pass NodeProvider to metricstore Init
Setting the provider after Init left the checkpoint restore inside Init
without job information and forced callers to pre-call InitMetrics and
SetNodeProvider in the right order. Init now receives the provider as a
parameter and injects it before the restore, so the ordering is enforced
by the signature. SetNodeProvider remains for callers that do not run
Init (tests, -cleanup-checkpoints).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 15:44:54 +02:00
Aditya Ujeniya ea2bfbb6aa fix: wire NodeProvider into CLI checkpoint cleanup path
The -cleanup-checkpoints CLI flag called CleanupCheckpoints without
initializing the MemoryStore singleton or setting its NodeProvider,
so it silently fell back to legacy clean-everything behavior and
could delete checkpoints for hosts with running jobs. Inject the job
repository as NodeProvider the same way runServer does, so the CLI
path also skips used hosts. Also update SetNodeProvider's doc comment,
which only mentioned Free but is now also consulted by FromCheckpoint
and CleanupCheckpoints.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 14:31:26 +02:00
Aditya Ujeniya 58c71da0b8 feat: skip checkpoint cleanup for nodes with running jobs 2026-07-08 14:13:15 +02:00
Aditya Ujeniya 1e416a6dd0 feat: load full checkpoint history for nodes with running jobs 2026-07-08 14:05:56 +02:00
Aditya Ujeniya 97ab828d60 feat: add isNodeUsed helper for NodeProvider lookups
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 14:01:37 +02:00
moebiusband 5d8d4e228e Merge branch 'main' into feature/526-average-resample 2026-06-17 06:30:54 +02:00
moebiusband 1b72b0b5ad Fix critical/severe issues in init, startup and shutdown
- auth: do not abort the server when authentication is disabled. auth.Init
  is now always called; with disable-authentication it sets up an ephemeral
  session store (SESSION_KEY not required) and registers no authenticators,
  so the unconditional auth.GetAuthInstance() callers (server init,
  api.New()) always get a valid instance.
- main: run the graceful-shutdown sequence on the startup-error path. runServer
  derives a cancelable context and, on a server-start failure, cancels it and
  waits so the metricstore final checkpoint / WAL rotation, archiver flush and
  taskmanager shutdown actually run before exit.
- server: log the :80 HTTP->HTTPS redirect listener error instead of dropping it.
- archiver: guard Shutdown against being called when Start never ran
  (avoids close(nil) panic / blocking on a nil workerDone).
- nats API: stop worker goroutines on shutdown via a stop channel + idempotent
  Shutdown(); workers and subscription callbacks select on stop and the
  channels are never closed, so no send-on-closed-channel can occur. Wired
  into Server.Shutdown after the NATS client is closed.
- metricstore: make Shutdown idempotent (nil shutdownFunc, early return) and
  release shutdownFuncMu before the checkpoint write.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 3c179f9caa8f
2026-06-05 10:16:28 +02:00
moebiusband 8733497223 Fix bug that prevents WAL cleanup on shutdown
Entire-Checkpoint: 5b79628feea4
2026-06-05 08:39:49 +02:00
moebiusband 6d86690c76 Fix critical issues from follow-up security audit
A second-pass audit surfaced three severe issues missed by the previous
review, each a sibling code path of a bug class that was only partially
fixed before:

- auth: JWT session login (jwtSession.go) registered its authenticator
  even when CROSS_LOGIN_JWT_HS512_KEY was unset, leaving an empty HMAC
  key. golang-jwt verifies any HS256/HS512 signature against an empty
  key, allowing unauthenticated admin token forgery. Init() now refuses
  to register without a key, with a defense-in-depth empty-key guard in
  the keyfunc.

- repository: metric names from GraphQL ([String!]) were interpolated
  raw into json_extract(footprint, "$.<name>") SQL. SQLite parses
  double-quoted strings as literals, enabling SQL injection by any
  authenticated user. Validate metric names against ^[a-zA-Z0-9_]+$ in
  jobsMetricStatisticsHistogram and buildFloatJSONCondition.

- metricstore: cluster/host line-protocol tags flowed unvalidated into
  path.Join(RootDir, cluster, host) for checkpoint/WAL files, allowing
  arbitrary file write outside the checkpoint root via NATS
  (unauthenticated) or POST /api/write. Reject path-traversal sequences
  in DecodeLine before the tags become path components.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: b57246993ec1
2026-06-04 19:07:20 +02:00
Thomas Roehl c76219651e Fix parsing of metric subtypes (key stype) 2026-05-04 18:10:01 +02:00
moebiusband 641dc0e3b8 Run gofumpt 2026-03-30 16:49:27 +02:00
moebiusband 66707bbf15 Update metricstore documentation
Entire-Checkpoint: 99f20c1edd90
2026-03-29 21:38:04 +02:00
moebiusband fc47b12fed fix: Pause WAL writes during binary checkpoint to prevent message drops
WAL writes during checkpoint are redundant since the binary snapshot
captures all in-memory data. Pausing eliminates channel saturation
(1.4M+ dropped messages) caused by disk I/O contention between
checkpoint writes and WAL staging. Also removes direct WAL file
deletion in checkpoint workers that raced with the staging goroutine.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 34d698f40bac
2026-03-29 11:13:39 +02:00
moebiusband 937984d11f fix: WAL rotation skipped for all nodes due to non-blocking send on small channel
RotateWALFiles used a non-blocking send (select/default) on rotation
channels buffered at 64. With thousands of nodes and few shards, the
channel fills instantly and nearly all hosts are skipped, leaving WAL
files unrotated indefinitely.

Replace with a blocking send using a shared 2-minute deadline so the
checkpoint goroutine waits for the staging goroutine to drain the
channel instead of immediately giving up.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: a1ec897216fa
2026-03-28 06:55:45 +01:00
moebiusband cc3d03bb5b fix: Unbound growth of wal files in case of checkpointing error
Entire-Checkpoint: 95a89a7127c5
2026-03-28 06:26:21 +01:00
moebiusband ac0a4cc39a Increase shutdown timeouts and WAL flush interval
Entire-Checkpoint: 94ee2fb97830
2026-03-27 09:56:34 +01:00
Aditya Ujeniya 6e97ac8b28 Verbose logs for DataDoesNotAlign error in CCMS 2026-03-26 14:13:12 +01:00
moebiusband 97d65a9e5c Fix bugs in WAL journal pipeline
Entire-Checkpoint: 8fe0de4e6ac2
2026-03-26 07:25:36 +01:00
moebiusband e759810051 Add shutdown timings. Do not drain WAL buffers on shutdown
Entire-Checkpoint: d4b497002f54
2026-03-26 07:02:37 +01:00
moebiusband 6f7dda53ee Cleanup
Entire-Checkpoint: ed68d32218ac
2026-03-24 07:03:46 +01:00
moebiusband 0325d9e866 fix: Increase throughput for WAL writers
Entire-Checkpoint: ddd40d290c56
2026-03-24 06:53:12 +01:00
moebiusband e41d1251ba fix: Continue on error
Entire-Checkpoint: 6000eb5a5bb8
2026-03-23 06:37:24 +01:00
moebiusband 586c902044 Restructure metricstore cleanup archiving to stay withinh 32k parquet-go limit
Entire-Checkpoint: 1660b8cf2571
2026-03-23 06:32:24 +01:00
moebiusband 01ec70baa8 Iterate over subCluster MetricConfig directly so that removed metrics are not included
Entire-Checkpoint: efb6f0a96069
2026-03-20 11:39:34 +01:00
moebiusband c0d2d65f96 Introduce average resampler support
Fixes #526

Entire-Checkpoint: 893a1de325b5
2026-03-19 21:16:48 +01:00
moebiusband 09501df3c2 fix: reduce memory usage in parquet checkpoint archiver
Stream CheckpointFile trees directly to parquet rows instead of
materializing all rows in a giant intermediate slice. This eliminates
~1.9GB per host of redundant allocations (repeated string headers)
and removes the expensive sort on millions of 104-byte structs.

Key changes:
- Replace flattenCheckpointFile + sortParquetRows + WriteHostRows with
  streaming WriteCheckpointFile that walks the tree with sorted keys
- Reduce results channel buffer from len(hostEntries) to 2 for
  back-pressure (at most NumWorkers+2 results in flight)
- Workers send CheckpointFile trees instead of []ParquetMetricRow
- Write rows in small 1024-element batches via reusable buffer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: f31dc1847539
2026-03-18 17:32:16 +01:00
moebiusband 8b132ed7f8 fix: Blocking ReceiveNats call
Entire-Checkpoint: 38a235c86ceb
2026-03-18 06:47:45 +01:00
moebiusband bf1a8a174e fix: Shard WAL consumer for higher throughput
Entire-Checkpoint: e583b7b11439
2026-03-18 06:32:14 +01:00
moebiusband 50aed595cf fix: metricstore NATS contention
Entire-Checkpoint: 7e68050cab59
2026-03-18 06:14:15 +01:00
moebiusband d46e6371fc Add log about checkpoint archiving
Entire-Checkpoint: bf29af79b268
2026-03-18 05:22:39 +01:00
moebiusband 02f82c2c0b fix: Prevent memory spikes in parquet writer for metricstore move policy
Entire-Checkpoint: 4a675b8352a2
2026-03-18 05:08:37 +01:00
moebiusband bab6eb4c3a Convert Warn message on missing metrics to Debug level 2026-03-16 15:35:24 +01:00
moebiusband 51517f8031 Reduce insert pressure in db. Increase sqlite timeout value
Entire-Checkpoint: a1e2931d4deb
2026-03-16 11:17:47 +01:00
moebiusband 39ab12784c Make checkpointInterval an option config option again.
Also applies small fixes

Entire-Checkpoint: c11d1a65fae4
2026-03-13 09:07:38 +01:00
moebiusband b214e1755a Add buffered I/O to WAL writes and fix MemoryCap comment
WAL writes now go through bufio.Writer instead of raw syscalls per record,
reducing I/O overhead. Buffers are flushed on rotate, drain, and shutdown.
Fixed misleading MemoryCap comment ("Max bytes" → "Max memory in GB").

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: b38dc35e5334
2026-03-13 09:05:24 +01:00
moebiusband 8234ad3126 fix: Fix metricstore memory explosion from broken emergency free and batch aborts
- Fix MemoryUsageTracker: remove premature bufferPool.Clear() that prevented
  mem.Alloc from decreasing, replace broken ForceFree loop (100 iterations
  with no GC) with progressive time-based Free at 75%/50%/25% retention,
  add bufferPool.Clear()+GC between steps so memory stats update correctly
- Enable debug.FreeOSMemory() after emergency freeing to return memory to OS
- Add adaptive ticker: 30s checks when memory >80% of cap, normal otherwise
- Reduce default memory check interval from 1h to 5min
- Don't abort entire NATS batch on single write error (out-of-order timestamp),
  log warning and continue processing remaining lines
- Prune empty levels from tree after free() to reduce overhead
- Include buffer struct overhead in sizeInBytes() for more accurate reporting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 7ce28627fc1d
2026-03-13 07:57:35 +01:00
moebiusband 845d0111af Further consolidate and improve ccms query builder
Entire-Checkpoint: d10e6221ee4f
2026-03-04 17:31:36 +01:00
moebiusband 26982088c3 Consolidate code for external and internal ccms buildQueries function
Entire-Checkpoint: fc3be444ef4c
2026-03-04 16:43:05 +01:00
Christoph Kluge 9672903d41 fix panic caused by concurrent map writes 2026-03-04 15:54:18 +01:00
moebiusband 67a17b5306 Reduce noise in info log 2026-03-04 15:14:35 +01:00