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

fix: remove turso client mutex — use per-request http clients

the shared mutex on Client.zig serialized ALL turso requests across
threads. during full sync, this blocked search queries from reaching
turso even when local sqlite wasn't ready yet.

now each request creates its own http client, so sync and search
can hit turso concurrently.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+10 -12
+10 -12
backend/src/db/Client.zig
··· 27 allocator: Allocator, 28 url: []const u8, 29 token: []const u8, 30 - mutex: std.Thread.Mutex = .{}, 31 - http_client: http.Client, 32 33 pub fn init(allocator: Allocator) !Client { 34 const url = std.posix.getenv("TURSO_URL") orelse { ··· 52 .allocator = allocator, 53 .url = host, 54 .token = token, 55 - .http_client = .{ .allocator = allocator }, 56 }; 57 } 58 59 pub fn deinit(self: *Client) void { 60 - self.http_client.deinit(); 61 } 62 63 pub fn query(self: *Client, comptime sql: []const u8, args: anytype) !Result { ··· 123 }); 124 defer span.end(); 125 126 - self.mutex.lock(); 127 - defer self.mutex.unlock(); 128 - 129 var url_buf: [URL_BUF_SIZE]u8 = undefined; 130 const url = std.fmt.bufPrint(&url_buf, "https://{s}/v2/pipeline", .{self.url}) catch 131 return error.UrlTooLong; ··· 140 var response_body: std.Io.Writer.Allocating = .init(self.allocator); 141 errdefer response_body.deinit(); 142 143 - const res = self.http_client.fetch(.{ 144 .location = .{ .url = url }, 145 .method = .POST, 146 .headers = .{ ··· 177 }); 178 defer span.end(); 179 180 - self.mutex.lock(); 181 - defer self.mutex.unlock(); 182 - 183 var url_buf: [URL_BUF_SIZE]u8 = undefined; 184 const url = std.fmt.bufPrint(&url_buf, "https://{s}/v2/pipeline", .{self.url}) catch 185 return error.UrlTooLong; ··· 194 var response_body: std.Io.Writer.Allocating = .init(self.allocator); 195 errdefer response_body.deinit(); 196 197 - const res = self.http_client.fetch(.{ 198 .location = .{ .url = url }, 199 .method = .POST, 200 .headers = .{
··· 27 allocator: Allocator, 28 url: []const u8, 29 token: []const u8, 30 31 pub fn init(allocator: Allocator) !Client { 32 const url = std.posix.getenv("TURSO_URL") orelse { ··· 50 .allocator = allocator, 51 .url = host, 52 .token = token, 53 }; 54 } 55 56 pub fn deinit(self: *Client) void { 57 + _ = self; 58 } 59 60 pub fn query(self: *Client, comptime sql: []const u8, args: anytype) !Result { ··· 120 }); 121 defer span.end(); 122 123 var url_buf: [URL_BUF_SIZE]u8 = undefined; 124 const url = std.fmt.bufPrint(&url_buf, "https://{s}/v2/pipeline", .{self.url}) catch 125 return error.UrlTooLong; ··· 134 var response_body: std.Io.Writer.Allocating = .init(self.allocator); 135 errdefer response_body.deinit(); 136 137 + // per-request http client — no shared mutex needed 138 + var hc: http.Client = .{ .allocator = self.allocator }; 139 + defer hc.deinit(); 140 + 141 + const res = hc.fetch(.{ 142 .location = .{ .url = url }, 143 .method = .POST, 144 .headers = .{ ··· 175 }); 176 defer span.end(); 177 178 var url_buf: [URL_BUF_SIZE]u8 = undefined; 179 const url = std.fmt.bufPrint(&url_buf, "https://{s}/v2/pipeline", .{self.url}) catch 180 return error.UrlTooLong; ··· 189 var response_body: std.Io.Writer.Allocating = .init(self.allocator); 190 errdefer response_body.deinit(); 191 192 + var hc: http.Client = .{ .allocator = self.allocator }; 193 + defer hc.deinit(); 194 + 195 + const res = hc.fetch(.{ 196 .location = .{ .url = url }, 197 .method = .POST, 198 .headers = .{