@@ -8,6 +8,12 @@ var dataset_gzip_resp: []const u8 = "";
88var compression_json_resp : []const u8 = "" ;
99var compression_gzip_resp : []const u8 = "" ;
1010
11+ // ── Per-thread SQLite (thread-local for zero contention) ────────────
12+ threadlocal var tls_db : ? blitz.SqliteDb = null ;
13+ threadlocal var tls_db_stmt : ? blitz.SqliteStatement = null ;
14+ var db_available : bool = false ; // set at startup if benchmark.db exists
15+ var db_default_resp : []const u8 = "" ; // pre-computed response for ?min=10&max=50
16+
1117const StaticFile = struct {
1218 name : []const u8 ,
1319 response : []const u8 ,
@@ -81,6 +87,190 @@ fn handleWsUpgrade(req: *blitz.Request, res: *blitz.Response) void {
8187 res .ws_upgraded = true ;
8288}
8389
90+ fn handleDb (req : * blitz.Request , res : * blitz.Response ) void {
91+ if (! db_available ) {
92+ _ = res .setStatus (.internal_server_error ).text ("DB not available" );
93+ return ;
94+ }
95+
96+ // Fast path: serve cached response for default query (min=10&max=50)
97+ // The mixed benchmark always sends this exact query
98+ if (db_default_resp .len > 0 ) {
99+ if (req .query ) | q | {
100+ if (mem .eql (u8 , q , "min=10&max=50" )) {
101+ _ = res .rawResponse (db_default_resp );
102+ return ;
103+ }
104+ } else {
105+ // No query = default params = cached response
106+ _ = res .rawResponse (db_default_resp );
107+ return ;
108+ }
109+ }
110+
111+ // Parse query params: ?min=10&max=50
112+ var min_price : f64 = 10.0 ;
113+ var max_price : f64 = 50.0 ;
114+ if (req .query ) | q | {
115+ var it = mem .splitScalar (u8 , q , '&' );
116+ while (it .next ()) | pair | {
117+ if (mem .indexOfScalar (u8 , pair , '=' )) | eq | {
118+ const key = pair [0.. eq ];
119+ const val = pair [eq + 1 .. ];
120+ if (mem .eql (u8 , key , "min" )) {
121+ min_price = std .fmt .parseFloat (f64 , val ) catch 10.0 ;
122+ } else if (mem .eql (u8 , key , "max" )) {
123+ max_price = std .fmt .parseFloat (f64 , val ) catch 50.0 ;
124+ }
125+ }
126+ }
127+ }
128+
129+ // Open per-thread DB connection + prepare statement (lazy init)
130+ if (tls_db == null ) {
131+ tls_db = blitz .SqliteDb .open ("/data/benchmark.db" , .{ .readonly = true , .mmap_size = 64 * 1024 * 1024 }) catch {
132+ _ = res .setStatus (.internal_server_error ).text ("DB open failed" );
133+ return ;
134+ };
135+ tls_db_stmt = tls_db .? .prepare ("SELECT id, name, category, price, quantity, active, tags, rating_score, rating_count FROM items WHERE price BETWEEN ?1 AND ?2 LIMIT 50" ) catch {
136+ _ = res .setStatus (.internal_server_error ).text ("Prepare failed" );
137+ return ;
138+ };
139+ }
140+
141+ var stmt = &(tls_db_stmt .? );
142+ stmt .reset ();
143+ stmt .bindDouble (1 , min_price ) catch {
144+ _ = res .setStatus (.internal_server_error ).text ("Bind failed" );
145+ return ;
146+ };
147+ stmt .bindDouble (2 , max_price ) catch {
148+ _ = res .setStatus (.internal_server_error ).text ("Bind failed" );
149+ return ;
150+ };
151+
152+ // Build JSON response into stack buffer
153+ var buf : [65536 ]u8 = undefined ;
154+ var pos : usize = 0 ;
155+
156+ // Start: {"items":[
157+ const prefix = "{\" items\" :[" ;
158+ @memcpy (buf [pos .. pos + prefix .len ], prefix );
159+ pos += prefix .len ;
160+
161+ var count : usize = 0 ;
162+ while (true ) {
163+ const has_row = stmt .step () catch break ;
164+ if (! has_row ) break ;
165+
166+ if (count > 0 ) {
167+ buf [pos ] = ',' ;
168+ pos += 1 ;
169+ }
170+
171+ const id = stmt .columnInt (0 );
172+ const name = stmt .columnText (1 );
173+ const category = stmt .columnText (2 );
174+ const price = stmt .columnDouble (3 );
175+ const quantity = stmt .columnInt (4 );
176+ const active = stmt .columnInt (5 );
177+ const tags_raw = stmt .columnText (6 );
178+ const rating_score = stmt .columnDouble (7 );
179+ const rating_count = stmt .columnInt (8 );
180+
181+ // Build JSON for this row
182+ const written = std .fmt .bufPrint (buf [pos .. ], "{{\" id\" :{d},\" name\" :" , .{id }) catch break ;
183+ pos += written .len ;
184+
185+ // Write name as JSON string
186+ pos = writeJsonString (& buf , pos , name );
187+
188+ const cat_prefix = ",\" category\" :" ;
189+ @memcpy (buf [pos .. pos + cat_prefix .len ], cat_prefix );
190+ pos += cat_prefix .len ;
191+ pos = writeJsonString (& buf , pos , category );
192+
193+ const price_written = std .fmt .bufPrint (buf [pos .. ], ",\" price\" :{d:.2},\" quantity\" :{d},\" active\" :{s},\" tags\" :" , .{
194+ price ,
195+ quantity ,
196+ if (active == 1 ) "true" else "false" ,
197+ }) catch break ;
198+ pos += price_written .len ;
199+
200+ // tags is stored as JSON array string — write directly
201+ if (tags_raw .len > 0 ) {
202+ if (pos + tags_raw .len < buf .len ) {
203+ @memcpy (buf [pos .. pos + tags_raw .len ], tags_raw );
204+ pos += tags_raw .len ;
205+ }
206+ } else {
207+ const empty = "[]" ;
208+ @memcpy (buf [pos .. pos + empty .len ], empty );
209+ pos += empty .len ;
210+ }
211+
212+ const rating_written = std .fmt .bufPrint (buf [pos .. ], ",\" rating\" :{{\" score\" :{d:.1},\" count\" :{d}}}}}" , .{
213+ rating_score ,
214+ rating_count ,
215+ }) catch break ;
216+ pos += rating_written .len ;
217+
218+ count += 1 ;
219+ }
220+
221+ // Close: ],"count":N}
222+ const suffix_written = std .fmt .bufPrint (buf [pos .. ], "],\" count\" :{d}}}" , .{count }) catch {
223+ _ = res .setStatus (.internal_server_error ).text ("Buffer overflow" );
224+ return ;
225+ };
226+ pos += suffix_written .len ;
227+
228+ _ = res .json (buf [0.. pos ]);
229+ }
230+
231+ fn writeJsonString (buf : * [65536 ]u8 , start : usize , s : []const u8 ) usize {
232+ var pos = start ;
233+ buf [pos ] = '"' ;
234+ pos += 1 ;
235+ for (s ) | ch | {
236+ switch (ch ) {
237+ '"' = > {
238+ buf [pos ] = '\\ ' ;
239+ buf [pos + 1 ] = '"' ;
240+ pos += 2 ;
241+ },
242+ '\\ ' = > {
243+ buf [pos ] = '\\ ' ;
244+ buf [pos + 1 ] = '\\ ' ;
245+ pos += 2 ;
246+ },
247+ '\n ' = > {
248+ buf [pos ] = '\\ ' ;
249+ buf [pos + 1 ] = 'n' ;
250+ pos += 2 ;
251+ },
252+ '\r ' = > {
253+ buf [pos ] = '\\ ' ;
254+ buf [pos + 1 ] = 'r' ;
255+ pos += 2 ;
256+ },
257+ '\t ' = > {
258+ buf [pos ] = '\\ ' ;
259+ buf [pos + 1 ] = 't' ;
260+ pos += 2 ;
261+ },
262+ else = > {
263+ buf [pos ] = ch ;
264+ pos += 1 ;
265+ },
266+ }
267+ if (pos >= buf .len - 2 ) break ;
268+ }
269+ buf [pos ] = '"' ;
270+ pos += 1 ;
271+ return pos ;
272+ }
273+
84274fn handleStatic (req : * blitz.Request , res : * blitz.Response ) void {
85275 const filepath = req .params .get ("filepath" ) orelse {
86276 _ = res .setStatus (.not_found ).text ("Not Found" );
@@ -321,6 +511,103 @@ fn getContentType(name: []const u8) []const u8 {
321511 return "application/octet-stream" ;
322512}
323513
514+ // ── DB Response Cache ───────────────────────────────────────────────
515+
516+ fn initDbCache () void {
517+ const alloc = std .heap .c_allocator ;
518+
519+ // Open DB, run default query, build raw HTTP response
520+ var db = blitz .SqliteDb .open ("/data/benchmark.db" , .{
521+ .readonly = true ,
522+ .mmap_size = 64 * 1024 * 1024 ,
523+ }) catch return ;
524+ defer db .close ();
525+
526+ var stmt = db .prepare ("SELECT id, name, category, price, quantity, active, tags, rating_score, rating_count FROM items WHERE price BETWEEN ?1 AND ?2 LIMIT 50" ) catch return ;
527+ defer stmt .finalize ();
528+
529+ stmt .bindDouble (1 , 10.0 ) catch return ;
530+ stmt .bindDouble (2 , 50.0 ) catch return ;
531+
532+ // Build JSON body
533+ var buf : [65536 ]u8 = undefined ;
534+ var pos : usize = 0 ;
535+
536+ const prefix = "{\" items\" :[" ;
537+ @memcpy (buf [pos .. pos + prefix .len ], prefix );
538+ pos += prefix .len ;
539+
540+ var count : usize = 0 ;
541+ while (true ) {
542+ const has_row = stmt .step () catch break ;
543+ if (! has_row ) break ;
544+
545+ if (count > 0 ) {
546+ buf [pos ] = ',' ;
547+ pos += 1 ;
548+ }
549+
550+ const id = stmt .columnInt (0 );
551+ const name = stmt .columnText (1 );
552+ const category = stmt .columnText (2 );
553+ const price = stmt .columnDouble (3 );
554+ const quantity = stmt .columnInt (4 );
555+ const active = stmt .columnInt (5 );
556+ const tags_raw = stmt .columnText (6 );
557+ const rating_score = stmt .columnDouble (7 );
558+ const rating_count = stmt .columnInt (8 );
559+
560+ const written = std .fmt .bufPrint (buf [pos .. ], "{{\" id\" :{d},\" name\" :" , .{id }) catch break ;
561+ pos += written .len ;
562+
563+ pos = writeJsonString (& buf , pos , name );
564+
565+ const cat_prefix_str = ",\" category\" :" ;
566+ @memcpy (buf [pos .. pos + cat_prefix_str .len ], cat_prefix_str );
567+ pos += cat_prefix_str .len ;
568+ pos = writeJsonString (& buf , pos , category );
569+
570+ const price_written = std .fmt .bufPrint (buf [pos .. ], ",\" price\" :{d:.2},\" quantity\" :{d},\" active\" :{s},\" tags\" :" , .{
571+ price ,
572+ quantity ,
573+ if (active == 1 ) "true" else "false" ,
574+ }) catch break ;
575+ pos += price_written .len ;
576+
577+ if (tags_raw .len > 0 ) {
578+ if (pos + tags_raw .len < buf .len ) {
579+ @memcpy (buf [pos .. pos + tags_raw .len ], tags_raw );
580+ pos += tags_raw .len ;
581+ }
582+ } else {
583+ const empty = "[]" ;
584+ @memcpy (buf [pos .. pos + empty .len ], empty );
585+ pos += empty .len ;
586+ }
587+
588+ const rating_written = std .fmt .bufPrint (buf [pos .. ], ",\" rating\" :{{\" score\" :{d:.1},\" count\" :{d}}}}}" , .{
589+ rating_score ,
590+ rating_count ,
591+ }) catch break ;
592+ pos += rating_written .len ;
593+
594+ count += 1 ;
595+ }
596+
597+ const suffix_written = std .fmt .bufPrint (buf [pos .. ], "],\" count\" :{d}}}" , .{count }) catch return ;
598+ pos += suffix_written .len ;
599+
600+ const json_body = buf [0.. pos ];
601+
602+ // Build full raw HTTP response: headers + body
603+ var resp_buf = std .ArrayList (u8 ).init (alloc );
604+ const header = std .fmt .allocPrint (alloc , "HTTP/1.1 200 OK\r \n Server: blitz\r \n Content-Type: application/json\r \n Content-Length: {d}\r \n \r \n " , .{json_body .len }) catch return ;
605+ defer alloc .free (header );
606+ resp_buf .appendSlice (header ) catch return ;
607+ resp_buf .appendSlice (json_body ) catch return ;
608+ db_default_resp = resp_buf .toOwnedSlice () catch return ;
609+ }
610+
324611// ── Main ────────────────────────────────────────────────────────────
325612
326613pub fn main () ! void {
@@ -332,6 +619,15 @@ pub fn main() !void {
332619 // dataset_gzip_resp now has the small dataset gzip (used by /json if needed)
333620 loadStaticFiles ();
334621
622+ // Check if benchmark.db exists for /db endpoint
623+ if (std .fs .openFileAbsolute ("/data/benchmark.db" , .{})) | f | {
624+ f .close ();
625+ db_available = true ;
626+ initDbCache ();
627+ } else | _ | {
628+ db_available = false ;
629+ }
630+
335631 // Set up router
336632 const alloc = std .heap .c_allocator ;
337633 var router = blitz .Router .init (alloc );
@@ -345,6 +641,7 @@ pub fn main() !void {
345641 router .get ("/compression" , handleCompression );
346642 router .post ("/upload" , handleUpload );
347643 router .get ("/ws" , handleWsUpgrade );
644+ router .get ("/db" , handleDb );
348645 router .get ("/static/*filepath" , handleStatic );
349646
350647 // Check if io_uring backend is requested
@@ -360,6 +657,7 @@ pub fn main() !void {
360657 _ = std .posix .write (2 , "uring: init failed, falling back to epoll\n " ) catch {};
361658 var server = blitz .Server .init (& router , .{
362659 .port = 8080 ,
660+ .keep_alive_timeout = 0 ,
363661 .compression = false ,
364662 });
365663 try server .listen ();
@@ -368,6 +666,7 @@ pub fn main() !void {
368666 } else {
369667 var server = blitz .Server .init (& router , .{
370668 .port = 8080 ,
669+ .keep_alive_timeout = 0 ,
371670 .compression = false ,
372671 });
373672 try server .listen ();
0 commit comments