Skip to content

Commit 69d4585

Browse files
committed
chore: bump version to 0.8.0-rc6 and implement performance optimizations
1 parent 0107a0e commit 69d4585

File tree

14 files changed

+109
-21
lines changed

14 files changed

+109
-21
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
111111
- Corrected `worker` service configuration (moved environment variables from healthcheck block and fixed `SERVICE_NAME`).
112112
- **Protocol Mismatch**: clarified requirement for `http` protocol in DSN when targeting local instances without SSL.
113113
- Admin chart missing metrics and live tail search filtering
114+
115+
### Optimized
116+
117+
- **Dashboard Performance**: implemented a multi-engine intelligent optimization strategy that makes project dashboards instant even with millions of logs.
118+
- **TimescaleDB Skip-Scan**: implemented Recursive CTEs for `distinct` queries, reducing execution time from minutes to milliseconds on high-cardinality fields like `service`.
119+
- **Intelligent Volume Estimation**: all engines now support `countEstimate`, allowing the dashboard to bypass heavy operations on high-volume projects.
120+
- **MongoDB Protection**: added safe timeouts and fallback logic for count operations on massive collections.
114121
- Golden signals: pass serviceName + attributes filter, parallelize fetches
115122
- ClickHouse `getMetricsOverview` alias collision
116123
- Sessions query using proper parameterized SQL

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@logtide/root",
3-
"version": "0.8.0-rc5",
3+
"version": "0.8.0-rc6",
44
"private": true,
55
"description": "LogTide - Self-hosted log management platform",
66
"author": "LogTide Team",

packages/backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@logtide/backend",
3-
"version": "0.8.0-rc5",
3+
"version": "0.8.0-rc6",
44
"private": true,
55
"description": "LogTide Backend API",
66
"type": "module",

packages/backend/src/modules/dashboard/service.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ class DashboardService {
141141

142142
/**
143143
* Get stats using continuous aggregates (fast path)
144+
*
145+
* PERFORMANCE: This is the critical path for dashboard speed.
146+
* 1. Uses pre-computed aggregates for 95% of the data.
147+
* 2. Uses countEstimate for the last hour to avoid scanning millions of rows on TimescaleDB.
148+
* 3. Falls back to exact count only if estimate is unavailable or volume is very low.
144149
*/
145150
private async getStatsFromAggregate(
146151
projectIds: string[],
@@ -149,7 +154,16 @@ class DashboardService {
149154
lastHourStart: Date,
150155
prevHourStart: Date
151156
): Promise<DashboardStats> {
152-
// Query aggregate for historical data (>1 hour old) and reservoir for recent data in parallel
157+
// 1. Get an initial estimate of the recent log volume.
158+
// countEstimate is fast on Timescale (EXPLAIN) and ClickHouse (Columnar).
159+
const recentTotalResult = await reservoir.countEstimate({
160+
projectId: projectIds,
161+
from: lastHourStart,
162+
to: new Date()
163+
});
164+
const recentVolume = recentTotalResult.count;
165+
166+
// 2. Query aggregate for historical data and reservoir for recent data in parallel
153167
const [todayAggregateStats, recentTotal, recentErrors, recentServices, yesterdayAggregateStats, prevHourCount] = await Promise.all([
154168
// Today's historical stats from aggregate (today start to 1 hour ago)
155169
db
@@ -165,9 +179,21 @@ class DashboardService {
165179
.executeTakeFirst(),
166180

167181
// Recent stats from reservoir (last hour)
168-
reservoir.count({ projectId: projectIds, from: lastHourStart, to: new Date() }),
169-
reservoir.count({ projectId: projectIds, from: lastHourStart, to: new Date(), level: ['error', 'critical'] }),
170-
reservoir.distinct({ field: 'service', projectId: projectIds, from: lastHourStart, to: new Date() }),
182+
// If volume is high (>50k), use the estimate we already got to save a query.
183+
recentVolume > 50000
184+
? Promise.resolve(recentTotalResult)
185+
: reservoir.count({ projectId: projectIds, from: lastHourStart, to: new Date() }),
186+
187+
// Errors in last hour: use estimate if volume is high
188+
recentVolume > 50000
189+
? reservoir.countEstimate({ projectId: projectIds, from: lastHourStart, to: new Date(), level: ['error', 'critical'] })
190+
: reservoir.count({ projectId: projectIds, from: lastHourStart, to: new Date(), level: ['error', 'critical'] }),
191+
192+
// Active services: scanning millions of rows for DISTINCT is the #1 cause of dashboard lag.
193+
// We skip real-time distinct if volume is high and rely on the aggregate data.
194+
recentVolume > 20000
195+
? Promise.resolve({ values: [], executionTimeMs: 0 })
196+
: reservoir.distinct({ field: 'service', projectId: projectIds, from: lastHourStart, to: new Date() }),
171197

172198
// Yesterday's stats from aggregate
173199
db
@@ -182,8 +208,10 @@ class DashboardService {
182208
.where('bucket', '<', todayStart)
183209
.executeTakeFirst(),
184210

185-
// Previous hour from reservoir (for throughput trend)
186-
reservoir.count({ projectId: projectIds, from: prevHourStart, to: lastHourStart }),
211+
// Previous hour count (for throughput trend)
212+
recentVolume > 50000
213+
? reservoir.countEstimate({ projectId: projectIds, from: prevHourStart, to: lastHourStart })
214+
: reservoir.count({ projectId: projectIds, from: prevHourStart, to: lastHourStart }),
187215
]);
188216

189217
// Combine aggregate + recent stats
@@ -192,7 +220,7 @@ class DashboardService {
192220
const yesterdayCount = Number(yesterdayAggregateStats?.total ?? 0);
193221
const yesterdayErrorCount = Number(yesterdayAggregateStats?.errors ?? 0);
194222

195-
// Approximate: aggregate distinct + recent distinct (may overcount)
223+
// Approximate: aggregate distinct + recent distinct (may overcount slightly)
196224
const todayServiceCount = Number(todayAggregateStats?.services ?? 0) + recentServices.values.length;
197225
const yesterdayServiceCount = Number(yesterdayAggregateStats?.services ?? 0);
198226

packages/backend/src/utils/internal-logger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export async function initializeInternalLogging(): Promise<string | null> {
5454
dsn,
5555
service: process.env.SERVICE_NAME || 'logtide-backend',
5656
environment: process.env.NODE_ENV || 'development',
57-
release: process.env.npm_package_version || '0.8.0-rc5', batchSize: 50,
57+
release: process.env.npm_package_version || '0.8.0-rc6', batchSize: 50,
5858
flushInterval: 10000,
5959
maxBufferSize: 5000,
6060
maxRetries: 2,

packages/frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@logtide/frontend",
3-
"version": "0.8.0-rc5",
3+
"version": "0.8.0-rc6",
44
"private": true,
55
"description": "LogTide Frontend Dashboard",
66
"type": "module",

packages/frontend/src/hooks.client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ if (dsn) {
99
dsn,
1010
service: 'logtide-frontend-client',
1111
environment: env.PUBLIC_NODE_ENV || 'production',
12-
release: env.PUBLIC_APP_VERSION || '0.8.0-rc5',
12+
release: env.PUBLIC_APP_VERSION || '0.8.0-rc6',
1313
debug: env.PUBLIC_NODE_ENV === 'development',
1414
browser: {
1515
// Core Web Vitals (LCP, INP, CLS, TTFB)

packages/frontend/src/hooks.server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export const handle = dsn
8282
dsn,
8383
service: 'logtide-frontend',
8484
environment: privateEnv?.NODE_ENV || 'production',
85-
release: process.env.npm_package_version || '0.8.0-rc5', }) as unknown as Handle,
85+
release: process.env.npm_package_version || '0.8.0-rc6', }) as unknown as Handle,
8686
requestLogHandle,
8787
configHandle
8888
)

packages/frontend/src/lib/components/Footer.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import Github from "@lucide/svelte/icons/github";
33
4-
const version = "Alpha v0.8.0-rc5";
4+
const version = "Alpha v0.8.0-rc6";
55
const currentYear = new Date().getFullYear();
66
const githubUrl = "https://github.com/logtide-dev/logtide";
77
</script>

packages/reservoir/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@logtide/reservoir",
3-
"version": "0.8.0-rc5",
3+
"version": "0.8.0-rc6",
44
"description": "Pluggable storage abstraction for Logtide log management",
55
"type": "module",
66
"main": "./dist/index.js",

0 commit comments

Comments
 (0)