diff --git a/sources/cockroachdb-patterns.md b/sources/cockroachdb-patterns.md deleted file mode 100644 index 6d822df..0000000 --- a/sources/cockroachdb-patterns.md +++ /dev/null @@ -1,179 +0,0 @@ -# Patterns Extracted from cockroachdb/cockroach - -## Pattern: Stopper for Goroutine Lifecycle - -**Source:** `pkg/util/stop/stopper.go` -**Category:** concurrency - -**What:** A dedicated struct that manages the lifecycle of -all goroutines in a component: tracks active tasks, refuses -new work during shutdown (quiesce), waits for completion, -then runs closers. - -**Why:** In distributed systems, clean shutdown is critical. -You need to: (1) stop accepting new work, (2) finish -in-flight work, (3) release resources in order. The Stopper -centralizes this instead of scattering shutdown logic across -every goroutine. - -**Example:** - -```go -type Stopper struct { - quiescer chan struct{} // closed when quiescing - stopped chan struct{} // closed when fully stopped - mu struct { - syncutil.RWMutex - _numTasks int32 - quiescing, stopping bool - closers []Closer - } -} - -// RunAsyncTask refuses new work during quiesce -func (s *Stopper) RunAsyncTask(ctx context.Context, - taskName string, f func(context.Context)) error { - if !s.addTask() { - return ErrUnavailable - } - go func() { - defer s.decTask() - f(ctx) - }() - return nil -} -``` - -**When to use:** Any server or subsystem that spawns -goroutines and needs graceful shutdown. Especially in -long-running services where leaked goroutines cause -resource exhaustion. - -**When NOT to use:** Simple programs with a single main -goroutine. Or when `errgroup` with context cancellation -suffices for the shutdown coordination. - ---- - -## Pattern: Tracked Lifecycle with Leak Detection - -**Source:** `pkg/util/stop/stopper.go` -**Category:** testing - -**What:** Register every Stopper instance in a global -tracker. In tests, call `PrintLeakedStoppers(t)` to detect -any Stopper that was created but never stopped — indicating -a resource leak. - -**Why:** Distributed systems have complex lifecycle graphs. -A forgot-to-stop bug silently leaks goroutines and -connections. The tracker makes leaks fail-loud in tests -without requiring careful manual cleanup. - -**Example:** - -```go -var trackedStoppers struct { - syncutil.Mutex - stoppers []stopperWithStack -} - -func register(s *Stopper) { - trackedStoppers.Lock() - trackedStoppers.stoppers = append(...) - trackedStoppers.Unlock() -} - -func PrintLeakedStoppers(t testing.TB) { - for _, tracked := range trackedStoppers.stoppers { - t.Errorf("leaked stopper, created at:\n%s", - tracked.createdAt) - } -} -``` - -**When to use:** Any resource that must be explicitly -closed/stopped and where forgetting to do so causes silent -degradation. - -**When NOT to use:** Resources with finalizers or GC-safe -cleanup. Adds global state — only for testing. - ---- - -## Pattern: Quiesce Then Stop (Two-Phase Shutdown) - -**Source:** `pkg/util/stop/stopper.go` -**Category:** concurrency - -**What:** Shutdown has two explicit phases: (1) Quiesce — -refuse new work, wait for in-flight to finish; (2) Stop — -run closers, signal done. Components observe -`ShouldQuiesce` channel alongside context. - -**Why:** One-phase shutdown (just cancel context) loses -in-flight work. Two-phase gives running tasks time to -complete while preventing new work from starting. The -explicit channel (vs just context) lets components -distinguish "winding down" from "dead." - -**Example:** - -```go -func worker(s *Stopper, ctx context.Context) { - for { - select { - case <-s.ShouldQuiesce(): - return // graceful: finish current, exit - case <-ctx.Done(): - return // hard cancel - case work := <-workChan: - process(work) - } - } -} -``` - -**When to use:** Servers handling requests where you want -zero-downtime deploys (drain then stop). Load balancers, -RPC servers, queue consumers. - -**When NOT to use:** Batch jobs or CLIs where immediate -exit is fine. - ---- - -## Pattern: CloserFn Adapter - -**Source:** `pkg/util/stop/stopper.go` -**Category:** concurrency - -**What:** Define a `Closer` interface with one method -(`Close()`), plus a `CloserFn` type that adapts any -function into a Closer. - -**Why:** The adapter pattern (like `http.HandlerFunc`) -avoids forcing users to define a struct just to implement -a one-method interface. Cleanup functions can be registered -directly. - -**Example:** - -```go -type Closer interface { Close() } -type CloserFn func() -func (f CloserFn) Close() { f() } - -// Usage: -stopper.AddCloser(stop.CloserFn(func() { - conn.Close() -})) -``` - -**When to use:** Any one-method interface where callers -often have a simple function they want to register. - -**When NOT to use:** Interfaces with >1 method, or when -the implementation needs state beyond a closure. - - diff --git a/sources/prometheus-patterns.md b/sources/prometheus-patterns.md deleted file mode 100644 index 6a63606..0000000 --- a/sources/prometheus-patterns.md +++ /dev/null @@ -1,182 +0,0 @@ -# Patterns Extracted from prometheus/prometheus - -## Pattern: Atomic File Operations with Suffix Convention - -**Source:** `tsdb/db.go` -**Category:** storage - -**What:** Use directory suffixes (`.tmp-for-creation`, -`.tmp-for-deletion`) to make multi-step file operations -crash-safe. On startup, clean up any dirs with these -suffixes (they represent incomplete operations). - -**Why:** Database storage needs atomicity. If the process -crashes between creating a block and finalizing it, you -need to know the block is incomplete. The suffix convention -makes incomplete state visible at the filesystem level -without requiring a separate journal. - -**Example:** - -```go -const ( - tmpForDeletionBlockDirSuffix = ".tmp-for-deletion" - tmpForCreationBlockDirSuffix = ".tmp-for-creation" -) - -// On startup: remove any .tmp-* dirs (incomplete ops) -// On create: write to dir.tmp-for-creation, then rename -// On delete: rename to dir.tmp-for-deletion, then remove -``` - -**When to use:** Any system that manages files/directories -and needs crash consistency without a full WAL. Simpler -than a write-ahead log for coarse-grained operations. - -**When NOT to use:** When you already have a WAL or -transaction log. Or for fine-grained operations where -rename semantics are insufficient. - ---- - -## Pattern: DefaultOptions() Function - -**Source:** `tsdb/db.go` -**Category:** configuration - -**What:** Provide a `DefaultOptions()` function returning a -fully-populated config struct. Users copy and override only -what they need. No nil-means-default ambiguity. - -**Why:** Large config structs (20+ fields) are unwieldy. -By providing sane defaults as a function (not a -package-level var), you avoid mutation bugs and make it -clear what "normal" looks like. Users only specify -deviations. - -**Example:** - -```go -func DefaultOptions() *Options { - return &Options{ - WALSegmentSize: wlog.DefaultSegmentSize, - RetentionDuration: int64(15*24*time.Hour / ...), - MinBlockDuration: DefaultBlockDuration, - MaxBlockDuration: DefaultBlockDuration, - SamplesPerChunk: DefaultSamplesPerChunk, - // ... 20 more fields with sane defaults - } -} - -// Usage: -opts := tsdb.DefaultOptions() -opts.RetentionDuration = 30 * 24 * time.Hour -db, err := tsdb.Open(dir, nil, nil, opts, nil) -``` - -**When to use:** Config structs with many fields where most -users want defaults. Especially when zero-value semantics -would be confusing (e.g., 0 retention = infinite? or off?). - -**When NOT to use:** Small configs (3-4 fields) where -struct literal with zero-means-default is clear enough. - ---- - -## Pattern: Scrape Loop with Aligned Timestamps - -**Source:** `scrape/scrape.go` -**Category:** concurrency - -**What:** Periodic scrape loops that align timestamps to -intervals with a small tolerance, enabling better storage -compression downstream. - -**Why:** Time-series databases compress better when -timestamps are regular. A 2ms tolerance on alignment -means scraped data aligns to the expected grid while -accommodating real-world jitter. - -**Example:** - -```go -var ScrapeTimestampTolerance = 2 * time.Millisecond -var AlignScrapeTimestamps = true - -// In scrape loop: if scrape finishes within tolerance -// of expected timestamp, snap to the grid -``` - -**When to use:** Any periodic data collection where -downstream storage benefits from timestamp regularity. -Metrics, heartbeats, polling loops. - -**When NOT to use:** Event-driven data where timestamps -must reflect actual occurrence time. Audit logs, user -actions, financial transactions. - ---- - -## Pattern: Sentinel Errors with Interface Check - -**Source:** `tsdb/db.go` -**Category:** error-handling - -**What:** Define package-level sentinel errors with -`errors.New()` and use compile-time interface assertions -to verify implementations satisfy storage interfaces. - -**Why:** `ErrNotReady` as a sentinel lets callers use -`errors.Is` for retry logic. The pattern ensures error -identity is stable across versions (not string-matched). - -**Example:** - -```go -var ErrNotReady = errors.New("TSDB not ready") - -// Callers can reliably detect this: -if errors.Is(err, tsdb.ErrNotReady) { - // Retry later — DB is still initializing -} -``` - -**When to use:** Any error that callers need to handle -programmatically (retry, fallback, special UI). Make it a -named sentinel, not a string comparison. - -**When NOT to use:** Errors that are always terminal or -always logged-and-discarded. Not every error needs a name. - ---- - -## Pattern: Compile-Time Interface Satisfaction - -**Source:** `scrape/scrape.go` -**Category:** organization - -**What:** Use `var _ Interface = (*Type)(nil)` to verify at -compile time that a type satisfies an interface, even if -the type is only used dynamically. - -**Why:** Without this, you discover missing methods only -when the type is actually used — which might be in a -rarely-exercised code path or only in production. The -compile-time check catches it immediately. - -**Example:** - -```go -var _ FailureLogger = (*logging.JSONFileLogger)(nil) -// Fails at compile time if JSONFileLogger doesn't -// implement FailureLogger -``` - -**When to use:** Any type that implements an interface -consumed dynamically (registered in a map, stored as -interface value, passed to framework code). - -**When NOT to use:** Types whose interface satisfaction is -already enforced by direct usage in the same package. - - diff --git a/sources/temporal-patterns.md b/sources/temporal-patterns.md deleted file mode 100644 index f283601..0000000 --- a/sources/temporal-patterns.md +++ /dev/null @@ -1,323 +0,0 @@ -# Patterns from Temporal (temporalio/temporal) - -Source: github.com/temporalio/temporal -Analyzed: 2026-04-30 -Commits: 8,958 | Contributors: 290 - ---- - -## 1. Effect Buffer (Transactional Side Effects) - -**Location:** `common/effect/buffer.go` - -Buffer side effects during a transaction, apply after -commit, rollback (in reverse order) after failure. - -```go -type Buffer struct { - effects []func(context.Context) - cancels []func(context.Context) -} - -func (b *Buffer) OnAfterCommit(effect func(ctx)) { - b.effects = append(b.effects, effect) -} - -func (b *Buffer) OnAfterRollback(effect func(ctx)) { - b.cancels = append(b.cancels, effect) -} - -func (b *Buffer) Apply(ctx context.Context) bool { - b.cancels = nil - for _, effect := range b.effects { - effect(ctx) // FIFO order - } - b.effects = nil - return true -} - -func (b *Buffer) Cancel(ctx context.Context) bool { - b.effects = nil - for i := len(b.cancels) - 1; i >= 0; i-- { - b.cancels[i](ctx) // LIFO (reverse) order - } - b.cancels = nil - return true -} -``` - -**When to use:** Any operation that has side effects -(notifications, cache invalidation, external calls) -that should only execute if the primary transaction -succeeds. - -**When NOT to use:** Simple operations where failure -leaves no cleanup needed. Over-engineering for -functions with a single side effect. - ---- - -## 2. Soft Assertions (Log, Don't Crash) - -**Location:** `common/softassert/softassert.go` - -Assert invariants in production code without panicking. -Log the violation so tests catch it but production -continues serving. - -```go -func That(logger log.Logger, condition bool, - staticMessage string, tags ...tag.Tag) bool { - if !condition { - logger.Error("failed assertion: "+staticMessage, - append([]tag.Tag{tag.FailedAssertion}, - tags...)...) - } - return condition -} -``` - -Usage at call sites: -```go -if !softassert.That(logger, obj.State == "ready", - "object not ready before dispatch") { - return // or take recovery action -} -``` - -**The philosophy (from PR #7411):** "Why not panic? -Maybe in the future. For now, we're happy with finding -these failed assertions in functional tests." - -**When to use:** Invariants that should always hold -but where crashing production is worse than logging. -Distributed systems where one node's invariant -violation shouldn't cascade. - -**When NOT to use:** Safety-critical paths where -corruption would be worse than downtime. Better to -crash than serve corrupt data. - ---- - -## 3. Type-Safe State Transitions - -**Location:** `service/history/hsm/sm.go` - -State machines with compile-time source state -validation and typed events. - -```go -type Transition[S comparable, SM StateMachine[S], E any] struct { - Sources []S - Destination S - apply func(SM, E) (TransitionOutput, error) -} - -func (t Transition[S, SM, E]) Apply(sm SM, event E) (TransitionOutput, error) { - if !slices.Contains(t.Sources, sm.State()) { - return TransitionOutput{}, - fmt.Errorf("%w from %v: %v", - ErrInvalidTransition, sm.State(), event) - } - sm.SetState(t.Destination) - return t.apply(sm, event) -} -``` - -**When to use:** Any system with defined state -lifecycles — workflow engines, order processing, -connection state, protocol implementations. - -**When NOT to use:** Simple boolean flags, systems -where state changes are free-form or user-defined. - ---- - -## 4. Mutable vs Immutable Context (Type-Level Access Control) - -**Location:** `chasm/context.go` - -Separate `Context` (read-only) from `MutableContext` -(read-write) at the type level. Functions that only -read take `Context`; functions that mutate take -`MutableContext`. - -```go -// Read-only operations -func (h *Handler) Check(ctx chasm.Context, c Component) (bool, error) - -// Mutation operations -func (h *Handler) Execute(ctx chasm.MutableContext, c Component) error -``` - -**From PR discussion (Sushisource):** "I think I prefer -them separate, because what happens if you mutate -something and then say 'not ready'? That would be some -weird violation that shouldn't be possible, and separate -contexts enforces that at the type level." - -**When to use:** Any system where read vs write access -matters — databases, caches, state machines, APIs with -both query and mutation. - -**When NOT to use:** Simple CRUD where every operation -is both a read and a write. - ---- - -## 5. Goroutine Handle (Safe Lifecycle) - -**Location:** `common/goro/goro.go` - -A handle to a goroutine that supports safe multi-stop, -context cancellation, and error collection. - -```go -h := goro.NewHandle(ctx) -h.Go(func(ctx context.Context) error { - for { - select { - case <-ctx.Done(): - return ctx.Err() - case task := <-ch: - process(task) - } - } -}) - -// Safe to call multiple times: -h.Cancel() -err := h.Wait() -``` - -**Born from:** PR #1892, fixing a double-close panic in -the task writer. The old code used a raw `chan struct{}` -for shutdown, which panics if closed twice. - -**When to use:** Any long-lived goroutine that needs -clean shutdown, error reporting, or may be stopped from -multiple places. - -**When NOT to use:** Fire-and-forget goroutines, or -goroutines managed by higher-level frameworks -(errgroup, worker pools). - ---- - -## 6. Dynamic Config with Type-Safe Generics - -**Location:** `common/dynamicconfig/` - -566 settings, each declared as a typed constant with -default value, description, and precedence resolution. - -```go -var WorkflowMaxTimeout = NewNamespaceDurationSetting( - "limit.maxWorkflowTimeout", - 24*365*time.Hour, - `Maximum timeout for a workflow execution.`, -) - -// Consumer side: -timeout := dc.GetDurationProperty( - dynamicconfig.WorkflowMaxTimeout, - namespace, -)() -``` - -Resolution order: task queue → namespace → global. -Uses `weak.Pointer` cache for GC-friendly memoization. - -**When to use:** Large systems with many operational -knobs that need to change without restart. Multi-tenant -systems where different tenants need different limits. - -**When NOT to use:** Simple services with <10 config -values. Static configuration is simpler and more -predictable. - ---- - -## 7. Composable Predicates (Filter Algebra) - -**Location:** `common/predicates/` - -Generic predicate interface with And/Or/Not composition -and automatic flattening. - -```go -type Predicate[T any] interface { - Test(T) bool - Equals(Predicate[T]) bool - Size() int -} - -// Usage: -filter := predicates.And( - predicates.Not(isExpired), - predicates.Or(isHighPriority, isRetryable), -) -``` - -Flattening: `And(And(a, b), c)` → `And(a, b, c)`. -Short-circuit: `And(Empty, anything)` → `Empty`. - -**When to use:** Task queues, query builders, access -control rules — anywhere filters compose. - -**When NOT to use:** Single predicate checks, simple -if-statements. - ---- - -## 8. Persistence Plugin Registration (init pattern) - -**Location:** `common/persistence/sql/` - -```go -func init() { - sql.RegisterPlugin("postgres12", &plugin{ - driver: &driver.PQDriver{}, - }) -} -``` - -Import-driven registration. The main binary imports the -plugins it wants; each plugin's init() registers it. - -**When to use:** Database drivers, encoding formats, -protocol implementations — anything with a fixed set -of implementations selected at compile time. - -**When NOT to use:** When you need runtime plugin -discovery (use a factory pattern instead). When the -number of implementations is small and stable (just -use a switch). - ---- - -## 9. ShutdownOnce (CAS-Based Safe Close) - -**Location:** `common/channel/shutdown_once.go` - -```go -func (c *ShutdownOnceImpl) Shutdown() { - if atomic.CompareAndSwapInt32( - &c.status, statusOpen, statusClosed) { - close(c.channel) - } -} -``` - -Wraps the "close of closed channel" panic with a CAS -guard. Safe to call from multiple goroutines. - -**When to use:** Any shared shutdown signal where -multiple goroutines might initiate shutdown. Replaces -`sync.Once` when you also need a channel for select. - -**When NOT to use:** When only one goroutine ever -triggers shutdown. When `context.WithCancel` suffices. - -