api

package
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 16, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Handler

type Handler struct {
	DB               *database.Database
	DBType           string
	Archive          *jsonarchive.Generator
	ArchiveFrequency jsonarchive.Frequency
	Limiter          *RateLimiter
	Logf             func(string, ...any)
	Cache            *ResponseCache
	TrackInfo        *TrackInfoCache // Cache expensive COUNT(DISTINCT) metadata so /api stays fast even on huge datasets.
}

Handler wires together the database and archive generator so HTTP routes can stay small and focused on translating query parameters into the asynchronous building blocks behind the scenes.

func NewHandler

func NewHandler(db *database.Database, dbType string, archive *jsonarchive.Generator, limiter *RateLimiter, logf func(string, ...any), freq jsonarchive.Frequency) *Handler

NewHandler constructs a Handler with sane defaults. Logf is optional; pass nil if logging is not required.

func (*Handler) Register

func (h *Handler) Register(mux *http.ServeMux)

Register attaches API routes to the provided mux. We keep the method tiny and declarative: it simply wires URLs to helpers, avoiding clever routing that could obscure how pages are served.

type Permit

type Permit struct {
	WaitNotice   bool
	WaitDuration time.Duration
	// contains filtered or unexported fields
}

Permit represents an acquired slot for a particular request. Call Release when the handler finished processing so the next queued request can proceed.

func (*Permit) Release

func (p *Permit) Release()

Release signals the associated limiter goroutine that the request is done. We set the channel to nil so double releases are harmless, following the Go proverb "A little copying is better than a little dependency".

type RateLimiter

type RateLimiter struct {
	// contains filtered or unexported fields
}

RateLimiter coordinates per-IP request sequencing without relying on mutexes. Each IP gets its own goroutine so the design follows "Do not communicate by sharing memory; share memory by communicating".

func NewRateLimiter

func NewRateLimiter(heavyCooldown time.Duration) *RateLimiter

NewRateLimiter constructs a limiter with the provided cooldown for heavy endpoints. The limiter immediately starts its coordination goroutine so the caller can use it without additional plumbing.

func (*RateLimiter) Acquire

func (l *RateLimiter) Acquire(ctx context.Context, ip string, kind RequestKind) (*Permit, error)

Acquire reserves a slot for the given IP and request kind. The returned Permit must be released once the handler is done. If the context is cancelled before the permit becomes available an error is returned.

type RequestKind

type RequestKind int

RequestKind distinguishes between lightweight metadata calls and heavy responses that stream larger payloads. This keeps the limiter expressive while staying simple to reason about.

const (
	// RequestGeneral marks inexpensive metadata lookups that still benefit
	// from the per-IP queue so clients cannot overwhelm the server with
	// concurrent requests.
	RequestGeneral RequestKind = iota
	// RequestHeavy marks endpoints that stream large responses. We enforce a
	// cooldown after each heavy call to prevent repeated downloads from a
	// single IP.
	RequestHeavy
)

type ResponseCache

type ResponseCache struct {
	// contains filtered or unexported fields
}

ResponseCache keeps expensive API responses in memory so identical requests within the TTL avoid hitting the database. We implement it with a dedicated goroutine and channels to honour the constraint of coordinating state without mutexes.

func NewResponseCache

func NewResponseCache(ttl time.Duration) *ResponseCache

NewResponseCache starts the caching goroutine immediately. Callers may pass nil to disable caching entirely. The clock is injectable for tests; in production we default to time.Now.

func (*ResponseCache) Close

func (c *ResponseCache) Close()

Close stops the cache goroutine. The method is safe to call multiple times; subsequent calls have no effect.

func (*ResponseCache) Get

func (c *ResponseCache) Get(ctx context.Context, key string, loader func(context.Context) ([]byte, error)) ([]byte, error)

Get returns cached bytes for the provided key or invokes loader to produce them. We copy the stored slice before returning so callers can safely modify the result without affecting future hits.

func (*ResponseCache) GetWithTTL

func (c *ResponseCache) GetWithTTL(ctx context.Context, key string, ttl time.Duration, loader func(context.Context) ([]byte, error)) ([]byte, error)

GetWithTTL behaves like Get but allows callers to override the cache TTL for a single lookup.

type TrackInfoCache

type TrackInfoCache struct {
	// contains filtered or unexported fields
}

TrackInfoCache shields handlers from heavy COUNT(DISTINCT) queries by caching the result and refreshing it in the background. Following the proverb "Don't communicate by sharing memory; share memory by communicating", we coordinate exclusively via channels.

func NewTrackInfoCache

func NewTrackInfoCache(db *database.Database, dbType string, ttl, timeout, retry time.Duration, logf func(string, ...any)) *TrackInfoCache

NewTrackInfoCache starts the cache goroutine. ttl controls how long results stay fresh, timeout bounds database calls and retry decides how quickly we attempt to recover after failures. Passing nil db disables the cache so handlers can fall back to direct queries.

func (*TrackInfoCache) Close

func (c *TrackInfoCache) Close()

Close stops the goroutine. The method is idempotent so shutdown paths stay simple.

func (*TrackInfoCache) Get

func (c *TrackInfoCache) Get(ctx context.Context) (int64, string, error)

Get returns the cached track count and latest ID. The call blocks until the first snapshot is ready or the context is cancelled.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL