Database Adapter
LoadFlux uses a DatabaseAdapter interface to abstract database operations. This makes it possible to support multiple databases (SQLite, MongoDB) and allows advanced users to implement custom adapters.
The canonical definition lives in src/types.ts. Only LoadFluxConfig and DatabaseAdapter are re-exported from the loadflux package.
DatabaseAdapter interface
interface MetricsQueryOptions {
/** Filter to a single instance; omit for all instances. */
instanceId?: string;
/** Aggregate metrics across all instances (cluster view). */
clusterAggregate?: boolean;
}
interface QueryFilter {
search?: string;
status?: string;
}
interface PaginationParams {
page: number;
limit: number;
}
interface PaginatedResult<T> {
data: T[];
pagination: {
page: number;
limit: number;
total: number;
totalPages: number;
hasNext: boolean;
hasPrev: boolean;
};
}
interface LiveOverviewMetrics {
rps: number;
rpm: number;
total_requests: number;
total_errors: number;
error_rate: number;
}
interface LifetimeTotals {
total_requests: number;
total_errors: number;
}
interface DatabaseAdapter {
connect(): Promise<void>;
close(): Promise<void>;
// Inserts (fire-and-forget for both adapters)
insertSystemMetrics(metrics: SystemMetricRow): void;
insertProcessMetrics(metrics: ProcessMetricRow): void;
insertEndpointMetricsBatch(rows: EndpointMetricRow[]): void;
insertError(error: ErrorLogRow): void;
insertErrorsBatch(errors: ErrorLogRow[]): void;
insertSystemAndProcessMetrics(
system: SystemMetricRow,
process: ProcessMetricRow,
): void;
getSystemMetrics(
range: TimeRange,
maxPoints?: number,
options?: MetricsQueryOptions,
): Promise<SystemMetricRow[]>;
getProcessMetrics(
range: TimeRange,
maxPoints?: number,
options?: MetricsQueryOptions,
): Promise<ProcessMetricRow[]>;
getLiveOverview(now: number): Promise<LiveOverviewMetrics>;
getLifetimeTotals(): Promise<LifetimeTotals>;
incrementLifetimeTotals(requests: number, errors: number): void;
getClusterSystemLive(lookbackMs: number): Promise<SystemMetricRow | null>;
getClusterProcessLive(lookbackMs: number): Promise<ProcessMetricRow | null>;
listInstances(range: TimeRange): Promise<string[]>;
countInstances(range: TimeRange): Promise<number>;
getEndpointMetrics(
range: TimeRange,
filter?: QueryFilter,
): Promise<EndpointMetricRow[]>;
getTopEndpoints(
metric: TopEndpointMetric,
limit: number,
range: TimeRange,
filter?: QueryFilter,
): Promise<TopEndpointRow[]>;
getSlowRequests(
thresholdMs: number,
range: TimeRange,
filter?: QueryFilter,
): Promise<EndpointMetricRow[]>;
getErrorLog(range: TimeRange, filter?: QueryFilter): Promise<ErrorLogRow[]>;
getErrorStatusCodes(range: TimeRange): Promise<number[]>;
getStatusDistribution(range: TimeRange): Promise<StatusDistribution>;
getOverview(range: TimeRange): Promise<OverviewMetrics>;
getSystemMetricsPaginated(
range: TimeRange,
pagination: PaginationParams,
maxPoints?: number,
options?: MetricsQueryOptions,
): Promise<PaginatedResult<SystemMetricRow>>;
getProcessMetricsPaginated(
range: TimeRange,
pagination: PaginationParams,
maxPoints?: number,
options?: MetricsQueryOptions,
): Promise<PaginatedResult<ProcessMetricRow>>;
getEndpointMetricsPaginated(
range: TimeRange,
pagination: PaginationParams,
filter?: QueryFilter,
): Promise<PaginatedResult<EndpointMetricRow>>;
getSlowRequestsPaginated(
thresholdMs: number,
range: TimeRange,
pagination: PaginationParams,
filter?: QueryFilter,
): Promise<PaginatedResult<EndpointMetricRow>>;
getErrorLogPaginated(
range: TimeRange,
pagination: PaginationParams,
filter?: QueryFilter,
): Promise<PaginatedResult<ErrorLogRow>>;
deleteOlderThan(timestamp: number): void;
getSetting(key: string): Promise<string | null>;
setSetting(key: string, value: string): void;
getUser(
username: string,
): Promise<{ username: string; password_hash: string } | null>;
hasAnyUser(): Promise<boolean>;
createUser(username: string, passwordHash: string): void;
updateUserPassword(username: string, passwordHash: string): void;
}
Built-in implementations
SQLiteAdapter
- Uses
better-sqlite3for synchronous, fast operations - Prepared statements for all queries
- Batched transactions for insert operations
- Automatic schema migrations on
connect() - File-based storage at the configured path
- Metric rows include optional
instance_id(used when cluster mode tags writes)
MongoDBAdapter
- Uses the official
mongodbdriver (peer dependency>= 5) - Collections:
system_metrics,process_metrics,endpoint_metrics,error_log,settings,auth - Retention uses the same cron-driven
deleteOlderThanas SQLite (not TTL indexes) - Aggregation pipelines for computed queries (top endpoints, overview, cluster rollups)
- Bulk writes via
insertMany; writes on the hot path are async (fireAndForget)
Method details
Lifecycle
connect()— Initialize the database connection. For SQLite, this opens the file and runs migrations. For MongoDB, this connects to the server and creates indexes.close()— Close the database connection gracefully.
Insert methods
Insert methods are synchronous (fire-and-forget). They don't return promises because:
- SQLite operations are synchronous by nature (
better-sqlite3) - MongoDB inserts are scheduled asynchronously and don't block the request path
insertEndpointMetricsBatch also calls incrementLifetimeTotals after persisting rows (both adapters).
Live vs historical metrics
getLiveOverview— Short-window RPS/RPM and related live fields used internally before the dashboard snapshot is built.getLifetimeTotals/incrementLifetimeTotals— O(1) counters for Total Requests and errors on the live dashboard. Seeded once from existingendpoint_metrics, then incremented on each flush. Not reduced when retention deletes old rows.getClusterSystemLive/getClusterProcessLive— Latest sample perinstance_id, then aggregated (cluster mode).
Time-series queries
getSystemMetrics/getProcessMetrics— OptionalmaxPointsdownsamples wide ranges server-side.MetricsQueryOptions— PassinstanceIdfor one replica, orclusterAggregate: truefor a cluster roll-up (the HTTP API sets this automatically whencluster.enabledandinstanceis omitted).
List and filter queries
getEndpointMetrics,getTopEndpoints,getSlowRequests,getErrorLog— OptionalQueryFilter:search(substring on path/method/message) andstatus(errors only).getErrorStatusCodes— Distinct HTTP status codes in the error log for a range (powers the Errors page status dropdown).listInstances/countInstances— Distinctinstance_idvalues in a time range.
Paginated queries
When the REST API receives page, list endpoints return PaginatedResult (data + pagination). Default limit is 200, max 1000.
Maintenance
deleteOlderThan(timestamp)— Delete all metric/error rows withtimestamp< cutoff. Invoked by the retention cron for both SQLite and MongoDB.
Settings
Key-value store for runtime configuration:
getSetting(key)/setSetting(key, value)
Used internally for schema_version, hmac_secret, retention_days, slow_threshold, and lifetime counter keys (lifetime_total_requests, lifetime_total_errors, lifetime_totals_seeded).
Auth
getUser(username)— Look up a user by usernamehasAnyUser()— Whether auth has been configuredcreateUser(username, passwordHash)— Create a new userupdateUserPassword(username, passwordHash)— Update an existing user's password hash
See REST API for how the dashboard calls these via HTTP.