-
Notifications
You must be signed in to change notification settings - Fork 0
Management
The extension adds a $timescale namespace for runtime management. It's also the way to manage policies on the manual config path (no annotations).
Typo-safe. The model/view names passed to every
$timescalemethod are checked against your registered hypertables and continuous aggregates —refreshContinuousAggregate("SensorHrly")is a compile error, not a runtime surprise. This works automatically from the generatedregistry(and from inlinetimescaledb({ hypertables: [...] })literals); only names built from runtimestringvariables fall back to uncheckedstring.
await prisma.$timescale.refreshContinuousAggregate("SensorHourly"); // full refresh
await prisma.$timescale.refreshContinuousAggregate("SensorHourly", { start, end }); // window
await prisma.$timescale.addContinuousAggregatePolicy("SensorHourly", {
startOffset: "1 month", endOffset: "1 hour", scheduleInterval: "1 hour",
});
await prisma.$timescale.removeContinuousAggregatePolicy("SensorHourly");If a scheduled refresh policy happens to be running on the same cagg, a manual refresh would otherwise fail with SQLSTATE 55P03 ("could not refresh … due to a concurrent refresh"). refreshContinuousAggregate retries this transient case with bounded exponential backoff (default up to 8 attempts); if contention persists, the original 55P03 is rethrown.
await prisma.$timescale.addRetentionPolicy("SensorReading", { dropAfter: "30 days" });
await prisma.$timescale.removeRetentionPolicy("SensorReading");
await prisma.$timescale.addCompressionPolicy("SensorReading", { after: "7 days", segmentBy: "deviceId", orderBy: "time DESC" });
await prisma.$timescale.removeCompressionPolicy("SensorReading");See Retention & compression for the annotation equivalents and the "changing an existing policy" caveats.
// List chunks (relation names), optionally bounded by interval — feeds compress/decompress/drop.
const chunks = await prisma.$timescale.showChunks("SensorReading"); // string[]
const old = await prisma.$timescale.showChunks("SensorReading", { olderThan: "30 days" });
// Convert a single chunk to/from the columnstore on demand (needs the columnstore enabled).
await prisma.$timescale.compressChunk(chunks[0]);
await prisma.$timescale.decompressChunk(chunks[0]);
// On-demand chunk drop (manual retention) — returns the dropped chunk names.
const dropped = await prisma.$timescale.dropChunks("SensorReading", { olderThan: "30 days" });
// Size, row-count & compression introspection — sizes/counts come back as exact `bigint`.
const bytes = await prisma.$timescale.hypertableSize("SensorReading"); // bigint
const detail = await prisma.$timescale.hypertableDetailedSize("SensorReading"); // { tableBytes, indexBytes, toastBytes, totalBytes }
const rowest = await prisma.$timescale.approximateRowCount("SensorReading"); // bigint (planner estimate; fast)
const stats = await prisma.$timescale.compressionStats("SensorReading"); // { totalChunks, compressedChunks, beforeTotalBytes, afterTotalBytes }showChunks returns show_chunks relation names (e.g. _timescaledb_internal._hyper_1_2_chunk) — pass one straight to compressChunk / decompressChunk (idempotent). Compressing a chunk requires the columnstore enabled on the hypertable (via @timescale.compression or addCompressionPolicy). showChunks / dropChunks bounds (olderThan / newerThan) are intervals relative to now (combine both for a window) — absolute-timestamp cutoffs aren't supported.
await prisma.$timescale.setChunkInterval("SensorReading", "6 hours");Changes the time-dimension chunk interval of a live hypertable (set_partitioning_interval). It affects only chunks created after the call — existing chunks keep their interval, so it never rewrites data. This is the runtime complement to the chunkInterval annotation (which only sets the size at create time).
Every policy this package creates (retention, compression, cagg refresh) runs as a TimescaleDB background job. These helpers inspect and control them — the "did my retention actually run?" / "pause compression for now" surface.
// Inspect — filter by model (a hypertable or a cagg), or omit for all jobs.
const jobs = await prisma.$timescale.listJobs("SensorReading"); // [{ jobId, procName, scheduleInterval, scheduled, config, nextStart, ... }]
const jstats = await prisma.$timescale.jobStats("SensorReading"); // last/next run, status, totalRuns/Successes/Failures (bigint)
const errs = await prisma.$timescale.jobErrors("SensorReading"); // recent failures (SQLSTATE + message), newest first
// Control — by numeric job id (from listJobs).
const { jobId } = jobs[0];
await prisma.$timescale.alterJob(jobId, { scheduled: false }); // pause
await prisma.$timescale.alterJob(jobId, { scheduled: true, scheduleInterval: "2 days" }); // resume + reschedule
await prisma.$timescale.runJob(jobId); // run now, synchronously
await prisma.$timescale.deleteJob(jobId); // remove a custom joblistJobs/jobStats/jobErrors read the timescaledb_information.jobs / job_stats / job_errors views and resolve the model name to its hypertable table or cagg view (so the filter spans both). alterJob only changes the fields you pass (scheduled, scheduleInterval, nextStart, maxRetries, retryPeriod, maxRuntime, config) and is a no-op if the job is already gone. For the package's own policies, prefer the dedicated remove*Policy methods over deleteJob; alterJob/runJob work on all of them by id.