search for standard sites pub-search.waow.tech
search zig blog atproto
11
fork

Configure Feed

Select the types of activity you want to include in your feed.

perf: route /tags and /popular to local SQLite

- sync popular_searches from Turso to local
- getTags() and getPopular() now try local first, fall back to Turso
- expected P95 improvement from ~1.5-2s to ~10-50ms

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

zzstoatzz af2e0e7f e9169b2c

+84 -1
+26 -1
backend/src/db/sync.zig
··· 113 113 } 114 114 } 115 115 116 + // sync popular searches 117 + var popular_count: usize = 0; 118 + { 119 + conn.exec("DELETE FROM popular_searches", .{}) catch {}; 120 + 121 + var popular_result = turso.query( 122 + "SELECT query, count FROM popular_searches", 123 + &.{}, 124 + ) catch |err| { 125 + std.debug.print("sync: turso popular_searches query failed: {}\n", .{err}); 126 + conn.exec("COMMIT", .{}) catch {}; 127 + local.setReady(true); 128 + return; 129 + }; 130 + defer popular_result.deinit(); 131 + 132 + for (popular_result.rows) |row| { 133 + conn.exec( 134 + "INSERT OR REPLACE INTO popular_searches (query, count) VALUES (?, ?)", 135 + .{ row.text(0), row.text(1) }, 136 + ) catch {}; 137 + popular_count += 1; 138 + } 139 + } 140 + 116 141 // record sync time 117 142 var ts_buf: [20]u8 = undefined; 118 143 const ts_str = std.fmt.bufPrint(&ts_buf, "{d}", .{std.time.timestamp()}) catch "0"; ··· 127 152 }; 128 153 129 154 local.setReady(true); 130 - std.debug.print("sync: full sync complete - {d} docs, {d} pubs, {d} tags\n", .{ doc_count, pub_count, tag_count }); 155 + std.debug.print("sync: full sync complete - {d} docs, {d} pubs, {d} tags, {d} popular\n", .{ doc_count, pub_count, tag_count, popular_count }); 131 156 } 132 157 133 158 /// Incremental sync: fetch documents created since last sync
+58
backend/src/stats.zig
··· 17 17 ); 18 18 19 19 pub fn getTags(alloc: Allocator) ![]const u8 { 20 + // try local SQLite first (faster) 21 + if (db.getLocalDb()) |local| { 22 + if (getTagsLocal(alloc, local)) |result| { 23 + return result; 24 + } else |_| {} 25 + } 26 + 27 + // fall back to Turso 20 28 const c = db.getClient() orelse return error.NotInitialized; 21 29 22 30 var output: std.Io.Writer.Allocating = .init(alloc); ··· 31 39 var jw: json.Stringify = .{ .writer = &output.writer }; 32 40 try jw.beginArray(); 33 41 for (res.rows) |row| try jw.write(TagJson{ .tag = row.text(0), .count = row.int(1) }); 42 + try jw.endArray(); 43 + return try output.toOwnedSlice(); 44 + } 45 + 46 + fn getTagsLocal(alloc: Allocator, local: *db.LocalDb) ![]const u8 { 47 + var output: std.Io.Writer.Allocating = .init(alloc); 48 + errdefer output.deinit(); 49 + 50 + var rows = try local.query( 51 + \\SELECT tag, COUNT(*) as count 52 + \\FROM document_tags 53 + \\GROUP BY tag 54 + \\ORDER BY count DESC 55 + \\LIMIT 100 56 + , .{}); 57 + defer rows.deinit(); 58 + 59 + var jw: json.Stringify = .{ .writer = &output.writer }; 60 + try jw.beginArray(); 61 + while (rows.next()) |row| { 62 + try jw.write(TagJson{ .tag = row.text(0), .count = row.int(1) }); 63 + } 34 64 try jw.endArray(); 35 65 return try output.toOwnedSlice(); 36 66 } ··· 159 189 } 160 190 161 191 pub fn getPopular(alloc: Allocator, limit: usize) ![]const u8 { 192 + // try local SQLite first (faster) 193 + if (db.getLocalDb()) |local| { 194 + if (getPopularLocal(alloc, local, limit)) |result| { 195 + return result; 196 + } else |_| {} 197 + } 198 + 199 + // fall back to Turso 162 200 const c = db.getClient() orelse return error.NotInitialized; 163 201 164 202 var output: std.Io.Writer.Allocating = .init(alloc); ··· 182 220 try jw.endArray(); 183 221 return try output.toOwnedSlice(); 184 222 } 223 + 224 + fn getPopularLocal(alloc: Allocator, local: *db.LocalDb, limit: usize) ![]const u8 { 225 + var output: std.Io.Writer.Allocating = .init(alloc); 226 + errdefer output.deinit(); 227 + 228 + _ = limit; // zqlite doesn't support runtime LIMIT, use fixed 10 229 + var rows = try local.query( 230 + "SELECT query, count FROM popular_searches ORDER BY count DESC LIMIT 10", 231 + .{}, 232 + ); 233 + defer rows.deinit(); 234 + 235 + var jw: json.Stringify = .{ .writer = &output.writer }; 236 + try jw.beginArray(); 237 + while (rows.next()) |row| { 238 + try jw.write(PopularJson{ .query = row.text(0), .count = row.int(1) }); 239 + } 240 + try jw.endArray(); 241 + return try output.toOwnedSlice(); 242 + }