Skip to content

feat: implement v2 interfaces across modules (v1.12.1)#85

Merged
intel352 merged 62 commits intomainfrom
feat/reimplementation
Mar 11, 2026
Merged

feat: implement v2 interfaces across modules (v1.12.1)#85
intel352 merged 62 commits intomainfrom
feat/reimplementation

Conversation

@intel352
Copy link
Contributor

Summary

Implements the new v2 enhancement interfaces (MetricsProvider, Drainable, Reloadable) across all applicable modules, targeting modular core v1.12.1.

Module Enhancements

Module MetricsProvider Drainable Reloadable
database ✅ pool stats ✅ drain connections ✅ pool settings
cache ✅ item count ✅ TTL, max items
eventbus ✅ delivery stats ✅ drain queues
httpserver ✅ server state ✅ drain flag ✅ timeouts
scheduler ✅ job counts ✅ persist + drain
reverseproxy ✅ request stats ✅ stop health checker
eventlogger ✅ flush outputs

Other Changes

  • All 14 modules bumped to modular v1.12.1
  • 35 new tests across 7 modules, all passing
  • Includes prior v2 core enhancements (12 features) and CI fixes

Test plan

  • All 14 modules build cleanly (go build ./...)
  • All new v2 interface tests pass
  • Core tests pass (go test ./...)
  • Compile-time interface assertions in all enhanced modules

🤖 Generated with Claude Code

intel352 and others added 30 commits March 9, 2026 18:23
Analyzed existing codebase against all 4 plans:
- TenantGuard: ~50% exists (context, service, config, decorators)
- Dynamic Reload: ~25% exists (observer, field tracking, config providers)
- Aggregate Health: ~15% exists (reverseproxy health checker)
- BDD/Contract Testing: ~65% exists (121 tests, contract CLI, CI)

Revised checklists to only cover remaining work.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… emission

Implements HealthProvider interface, HealthReport/AggregatedHealth types,
provider adapters (simple, static, composite), and AggregateHealthService
with fan-out evaluation, panic recovery, cache TTL, temporary error
detection, and CloudEvent emission on status changes. 17 tests including
concurrency and race-detector verified.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduces TenantGuard interface and StandardTenantGuard implementation
with strict/lenient/disabled modes, whitelist support, ring buffer
violation tracking, and CloudEvents emission on violations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…and rollback

Add Reloadable interface to module.go for modules that support runtime
config reloading. Implement ReloadOrchestrator with single-flight
execution, exponential backoff circuit breaker, reverse-order rollback
on partial failure, and CloudEvents emission for reload lifecycle.

New files:
- reload.go: ChangeType, ConfigChange, FieldChange, ConfigDiff, ReloadTrigger types
- reload_orchestrator.go: ReloadOrchestrator with queue, circuit breaker, rollback
- reload_test.go: comprehensive tests (ConfigDiff, orchestrator, rollback, circuit breaker, concurrency)

Also fix pre-existing WithLogger name collision in health_service.go -> WithHealthLogger.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace dynamic errors.New() with sentinel errors (err113)
- Add StatusUnknown case to exhaustive switch (exhaustive)
- Derive request context from parent context (contextcheck)
- Wrap interface method error return (wrapcheck)
- Fix gofmt alignment in builder.go, contract_verifier.go, observer.go
- Update letsencrypt go.sum for httpserver@v1.12.0 checksum

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reload orchestrator:
- Sort targets by module name for deterministic reload/rollback order
- Make Stop() idempotent with sync.Once, reject requests after stop
- Move noop check before started event to avoid misleading event stream
- Apply default 30s timeout when ReloadTimeout() returns <= 0
- Add doc comment noting application integration is a follow-up

Health service:
- Switch from *log.Logger to modular.Logger for consistent structured logging
- Return deep copies from Check() to prevent cache mutation by callers
- Use NewCloudEvent helper for proper event ID/specversion
- Prefer StatusUnhealthy over StatusUnknown on tie-breaks

Tenant guard:
- Switch from *log.Logger to modular.Logger for consistent structured logging
- Deep-copy whitelist in constructor, convert to set for O(1) lookups
- Use NewCloudEvent helper for proper event ID/specversion
- Use structured logging (Warn with key-value pairs) instead of Printf
- Wire guard into application service registry via builder

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add ErrReloadStopped for stopped orchestrator (distinct from channel full)
- Guard against nil logger with nopLogger fallback in NewReloadOrchestrator
- Fix BenchmarkReload to call processReload directly instead of queue+drain
- Update rollback test to assert deterministic sorted order

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- reload_orchestrator: add recover guard in RequestReload to prevent
  panic on send-to-closed-channel race with Stop()
- builder: use app.RegisterService() instead of direct SvcRegistry map
  mutation for tenant guard registration
- reload_contract_bdd_test: make rollback assertion deterministic now
  that targets are sorted by name

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- reload_orchestrator: extract handleReload() to properly scope context
  lifetime (no defer cancel leak in loop), propagate req.Ctx values and
  cancellation to module Reload calls instead of only copying deadline
- health_service: worstStatus now maps StatusUnknown → StatusUnhealthy
  in output, consistent with documented aggregation behavior
- reload_test: replace all time.Sleep waits with polling waitFor helper
  for deterministic CI-safe synchronization
- reload_contract_bdd_test: replace time.Sleep waits with event/call
  polling helpers (bddWaitForEvent, bddWaitForCalls)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keep context chain rooted in parentCtx and apply request deadline via
context.WithDeadline instead of swapping the base context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- contract_verifier: guard checkReloadIdempotent with goroutine+select
  so a misbehaving module cannot block the verifier indefinitely
- health_service: Check now selects on ctx.Done() when collecting
  provider results, returning ctx.Err() on cancellation/timeout
- tenant_guard: log NotifyObservers errors instead of silently dropping;
  update TenantGuardLenient doc to clarify logging is best-effort
- reload_contract_bdd_test: simulate elapsed backoff by backdating
  lastFailure instead of manually clearing circuit breaker state
- reload_orchestrator: propagate req.Ctx cancellation via background
  goroutine watching req.Ctx.Done(), not just deadline

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add ErrReloadTimeout sentinel error, use in contract verifier
- Wrap ctx.Err() in health_service.Check for wrapcheck compliance

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Covers 12 gaps identified in framework audit: config-driven deps,
drainable shutdown, phase tracking, parallel init, type-safe services,
service readiness events, plugin interface, reload integration,
config file watcher, secret resolution, slog adapter, metrics hooks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
12 tasks covering all audit gaps: config-driven deps, drainable
shutdown, phase tracking, parallel init, type-safe services,
service readiness, plugin interface, reload integration,
secret resolver, config watcher, slog adapter, metrics hooks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…hints

Allow injecting dependency edges into the module dependency graph from
the builder/config level, without requiring modules to implement the
DependencyAware interface. Hints are merged into the graph before
topological sort, enabling correct init ordering and cycle detection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ycle

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add sync.RWMutex to EnhancedServiceRegistry for thread-safe concurrent access
- Protect all registry methods (RegisterService, GetService, OnServiceReady, etc.)
- Fire readiness callbacks outside the lock to prevent deadlocks
- Add Init(modular.Application) to ConfigWatcher to satisfy Module interface
- Add ErrDynamicReloadNotEnabled sentinel error
- Add []any slice handling to ExpandSecrets for YAML array configs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
intel352 and others added 11 commits March 10, 2026 21:20
…events

- application.go: fix moduleRegistry race in initModule log line (use local var)
- application.go: guard CollectAllMetrics with initMu snapshot
- application.go: emit EventTypeAppPhaseChanged via phaseChangeHook in
  ObservableApplication
- application.go: add PhaseAware, ReloadableApp, MetricsCollector optional
  interfaces so callers don't need type assertions to *StdApplication
- service.go: restructure locking — registerAndNotify acquires/releases lock
  with defer-safe pattern, removing fragile caller-must-hold contract
- configwatcher: log watcher errors instead of silently discarding them
- configwatcher: check stopCh in AfterFunc callback to avoid firing onChange
  after shutdown
- configwatcher: add WithLogger option, capture app logger in Init
- configwatcher_test: check os.WriteFile errors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- builder.go: unwrap ApplicationDecorator chain to find the underlying
  StdApplication/ObservableApplication before propagating options
  (dependencyHints, drainTimeout, dynamicReload, parallelInit)
- application.go: validate that both From and To modules in dependency
  hints exist in moduleRegistry before merging into the graph

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds v2 enhancement interfaces to the eventbus module:
- MetricsProvider: exposes delivery stats (delivered, dropped, topics, subscribers)
- Drainable: PreStop signals drain phase before shutdown

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds v2 enhancement interfaces to the cache module:
- MetricsProvider: exposes item count, max items, connected status
- Reloadable: hot-reload TTL, max items, cleanup interval

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds v2 enhancement interfaces to the scheduler module:
- MetricsProvider: exposes job counts, worker count, running state
- Drainable: PreStop persists jobs and signals drain before shutdown

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…interfaces

Adds v2 enhancement interfaces to the database module:
- MetricsProvider: exposes sql.DBStats (connections, pool utilization)
- Drainable: PreStop drains active connections before shutdown
- Reloadable: hot-reload pool settings without reconnecting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds v2 enhancement interfaces to the reverseproxy module:
- MetricsProvider: exposes request counts, error counts, backend count
- Drainable: PreStop stops health checker during drain phase

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds v2 enhancement interfaces to the httpserver module:
- Drainable: PreStop signals drain phase before graceful shutdown
- Reloadable: hot-reload server timeouts without restart
- MetricsProvider: exposes server state (started, port)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 11, 2026 11:14
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR rolls out the new “v2 enhancement” interfaces (metrics, drain/pre-stop, reload) across the modular core and multiple modules, along with a broad modernization sweep (e.g., any, maps/slices, typed reflection) and accompanying tests/examples.

Changes:

  • Adds core support types/utilities (e.g., MetricsProvider, Drainable, Plugin, typed service helpers, secret expansion) and extends service registry behavior.
  • Implements v2 interfaces across several modules (scheduler/cache/eventbus/httpserver/reverseproxy/database/eventlogger) with new test coverage.
  • Updates Go/toolchain versions and CI workflows; updates modules/examples go.mod files and dependencies.

Reviewed changes

Copilot reviewed 164 out of 168 changed files in this pull request and generated 16 comments.

Show a summary per file
File Description
go.mod Bumps Go/toolchain; adds deps
go.sum Updates dependency checksums
service.go Adds locking + readiness callbacks + module registration helper
service_readiness_test.go Tests readiness callbacks
service_typed.go Adds typed service helpers
service_typed_test.go Tests typed service helpers
metrics.go Introduces module metrics interface/types
metrics_test.go Tests metrics aggregation
drainable.go Introduces drain phase interface + default timeout
drainable_test.go Tests drain ordering/timeout option
plugin.go Adds plugin interfaces + service definitions
plugin_test.go Tests plugin wiring (modules/services/hooks)
phase.go Adds lifecycle phase enum
phase_test.go Tests phase transitions/stringer
secret_resolver.go Adds secret reference expansion utility
secret_resolver_test.go Tests secret expansion behavior
builder.go Adds builder options (dependency hints/drain timeout/parallel init/dynamic reload/plugins)
builder_dependency_test.go Tests config-driven dependency edges
reload_orchestrator.go Updates event payload types to any
reload_test.go Updates tests (contexts/slices, concurrency helpers)
reload_contract_bdd_test.go Updates BDD assertions/concurrency patterns
reload_integration_test.go Adds integration coverage for dynamic reload wiring
contract_verifier.go Updates concurrency checks for reload contract
application_observer.go Adds phase-change CloudEvent emission + any updates
observer.go Adds app phase event type constant
observer_cloudevents.go Switches CloudEvent constructors to any/map[string]any
observer_cloudevents_test.go Updates tests for any payloads
observer_test.go Updates tests to any + slices.Contains
decorator_observable.go Updates event metadata types to any
config_feeders.go Switches feeder interfaces to any
config_provider.go Switches internal config maps/temps to any
config_validation.go Switches config APIs to any + typed reflection helpers
tenant_service.go Uses slices.Contains for duplicate module detection
tenant_guard_test.go Updates loops to integer ranging
tenant_config_loader_test.go Uses maps.Copy for map cloning
health_service.go Uses maps.Copy for snapshot/deep-copy
health_test.go Updates concurrency loops/helpers
modules/configwatcher/configwatcher.go Adds config file watcher module (fsnotify + debounce)
modules/configwatcher/configwatcher_test.go Tests file change + debounce behavior
modules/configwatcher/go.mod New module go.mod updates
modules/scheduler/module.go Implements MetricsProvider + Drainable (PreStop)
modules/scheduler/scheduler.go Adds gosec/contextcheck nolints
modules/scheduler/v2_interfaces_test.go Tests scheduler metrics + drain behavior
modules/scheduler/go.mod Bumps Go/toolchain + modular version
modules/cache/engine.go Extends cache engine with Stats()
modules/cache/memory.go Implements Stats()
modules/cache/redis.go Implements Stats()
modules/cache/v2_interfaces.go Adds metrics + reload support
modules/cache/v2_interfaces_test.go Tests cache metrics + reload
modules/cache/go.mod Bumps Go/toolchain + modular version
modules/eventbus/v2_interfaces.go Adds metrics + drain behavior
modules/eventbus/v2_interfaces_test.go Tests eventbus metrics + PreStop
modules/eventbus/* (memory/redis/nats/kafka/kinesis/etc.) Adds gosec nolints around cancel storage
modules/eventbus/go.mod Bumps Go/toolchain + modular version
modules/httpserver/module.go Adds draining flag field
modules/httpserver/v2_interfaces.go Adds drain/reload/metrics implementations
modules/httpserver/v2_interfaces_test.go Tests drain/reload/metrics behaviors
modules/httpserver/go.mod Bumps Go version + modular version
modules/reverseproxy/v2_interfaces.go Adds metrics + drain behavior
modules/reverseproxy/v2_interfaces_test.go Tests reverseproxy metrics + PreStop
modules/reverseproxy/module.go Moves proxy setup to Rewrite and adjusts proxy cloning
modules/reverseproxy/go.mod Bumps Go version + modular version
modules/database/v2_interfaces.go Adds metrics + drain + reload behavior
modules/database/v2_interfaces_test.go Tests db metrics/drain/reload
modules/database/go.mod Bumps Go version + modular version
modules/eventlogger/module.go Adds PreStop flush behavior
modules/eventlogger/v2_interfaces_test.go Tests drainable compliance
modules/eventlogger/go.mod Bumps Go/toolchain + modular version
modules/auth/go.mod Bumps Go version + modular version
modules/chimux/go.mod Bumps Go version + modular version
modules/httpclient/go.mod Bumps Go version + modular version
modules/jsonschema/go.mod Bumps Go/toolchain + modular version
modules/letsencrypt/go.mod Bumps Go version + modular/httpserver versions
modules/logmasker/go.mod Bumps Go version + modular version
examples/*/go.mod Bumps Go/toolchain versions in examples
examples/*/main.go Updates imports to database/v2 where applicable
.github/workflows/*.yml Updates CI Go version + workflow tweaks
docs/plans/2026-03-09-modular-v2-enhancements-design.md Adds design plan doc for v2 enhancements

Fix duplicate imports (slices, maps) introduced by merge and
resolve whitespace alignment conflicts in test files. Keep
v1.12.1 dependency + replace directives for all modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Contributor

github-actions bot commented Mar 11, 2026

📋 API Contract Changes Summary

No breaking changes detected - only additions and non-breaking modifications

Changed Components:

Core Framework

Contract diff saved to artifacts/diffs/core.json

Module: auth

Contract diff saved to artifacts/diffs/auth.json

Module: cache

Contract diff saved to artifacts/diffs/cache.json

Module: chimux

Contract diff saved to artifacts/diffs/chimux.json

Module: configwatcher

Contract diff saved to artifacts/diffs/configwatcher.json

Module: database

Contract diff saved to artifacts/diffs/database.json

Module: eventbus

Contract diff saved to artifacts/diffs/eventbus.json

Module: eventlogger

Contract diff saved to artifacts/diffs/eventlogger.json

Module: httpclient

Contract diff saved to artifacts/diffs/httpclient.json

Module: httpserver

Contract diff saved to artifacts/diffs/httpserver.json

Module: jsonschema

Contract diff saved to artifacts/diffs/jsonschema.json

Module: letsencrypt

Contract diff saved to artifacts/diffs/letsencrypt.json

Module: logmasker

Contract diff saved to artifacts/diffs/logmasker.json

Module: reverseproxy

Contract diff saved to artifacts/diffs/reverseproxy.json

Module: scheduler

Contract diff saved to artifacts/diffs/scheduler.json

Artifacts

📁 Full contract diffs and JSON artifacts are available in the workflow artifacts.

@codecov
Copy link

codecov bot commented Mar 11, 2026

Codecov Report

❌ Patch coverage is 86.50307% with 22 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
modules/cache/redis.go 0.00% 11 Missing ⚠️
modules/cache/v2_interfaces.go 86.66% 2 Missing and 2 partials ⚠️
modules/httpserver/v2_interfaces.go 93.54% 2 Missing and 2 partials ⚠️
modules/scheduler/module.go 91.17% 2 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 11, 2026 11:23
- Remove all `replace` directives from module go.mod files; local dev
  should use go.work (already gitignored) instead of shipping replace
  directives that break downstream consumers
- Run go mod tidy on all 14 modules to update go.sum
- Fix letsencrypt httpserver dep back to v1.12.0 (no v1.12.1 tag yet)
- Guard reflect.Type.Elem() in generateUniqueName for non-pointer types
  to prevent panic when module is passed as a value (not a pointer)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 33 out of 52 changed files in this pull request and generated 4 comments.

intel352 and others added 2 commits March 11, 2026 07:49
- Cache: protect config field reads/writes with configMu to prevent races
  between Reload() and concurrent Set()/Stats() calls. Lock MemoryCache
  mutex when updating MaxItems to synchronize with engine reads.
- Cache: remove cleanupInterval from Reload (ticker already running).
- httpserver: stop mutating m.server timeout fields in Reload to avoid
  data races on a running http.Server; update config only.
- reverseproxy: remove slog fallback in PreStop to use framework logger.
- Redis: replace PING-based Stats with pool statistics (no network I/O).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- httpserver: use static ErrServerNotStarted instead of dynamic
  fmt.Errorf to satisfy err113 lint rule.
- eventlogger: increase buffer sizes in SynchronousStartupConfigFlag
  test (5→50) and metadata BDD test (10→50) to absorb framework
  lifecycle events that fill small buffers under race-instrumented CI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 11, 2026 13:36
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 36 out of 55 changed files in this pull request and generated 7 comments.

- httpserver: protect m.started and m.config reads/writes with m.mu in
  CanReload, Reload, and CollectMetrics
- database: fix PreStop SetMaxOpenConns(0) which means unlimited, not
  zero — cap at max(stats.InUse, 1) to actually restrict the pool
- database: add connMu RWMutex to protect m.connections map iteration
  in CollectMetrics and PreStop
- cache: move c.config.MaxItems read inside c.mutex.RLock in Stats

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@intel352 intel352 merged commit 4a9fdbf into main Mar 11, 2026
87 checks passed
@intel352 intel352 deleted the feat/reimplementation branch March 11, 2026 14:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants