fix cache related segfault

This commit is contained in:
Emil Lerch 2026-01-02 15:06:35 -08:00
parent 7fd9810c78
commit ede8b593b8
Signed by: lobo
GPG key ID: A7B62D657EF764F8
2 changed files with 40 additions and 6 deletions

42
src/cache/Cache.zig vendored
View file

@ -14,18 +14,21 @@ pub const Config = struct {
cache_dir: []const u8, cache_dir: []const u8,
}; };
pub fn init(allocator: std.mem.Allocator, config: Config) !Cache { pub fn init(allocator: std.mem.Allocator, config: Config) !*Cache {
std.fs.makeDirAbsolute(config.cache_dir) catch |err| { std.fs.makeDirAbsolute(config.cache_dir) catch |err| {
if (err != error.PathAlreadyExists) return err; if (err != error.PathAlreadyExists) return err;
}; };
var cache = Cache{ const cache = try allocator.create(Cache);
errdefer allocator.destroy(cache);
cache.* = Cache{
.allocator = allocator, .allocator = allocator,
.lru = try Lru.init(allocator, config.max_entries), .lru = try Lru.init(allocator, config.max_entries),
.cache_dir = try allocator.dupe(u8, config.cache_dir), .cache_dir = try allocator.dupe(u8, config.cache_dir),
}; };
cache.lru.setEvictionCallback(&cache, evictCallback); cache.lru.setEvictionCallback(cache, evictCallback);
// Clean up expired files and populate L1 cache from L2 // Clean up expired files and populate L1 cache from L2
cache.loadFromDir() catch |err| { cache.loadFromDir() catch |err| {
@ -69,6 +72,7 @@ pub fn put(self: *Cache, key: []const u8, value: []const u8, ttl_seconds: u64) !
pub fn deinit(self: *Cache) void { pub fn deinit(self: *Cache) void {
self.lru.deinit(); self.lru.deinit();
self.allocator.free(self.cache_dir); self.allocator.free(self.cache_dir);
self.allocator.destroy(self);
} }
fn getCacheFilename(self: *Cache, key: []const u8) ![]const u8 { fn getCacheFilename(self: *Cache, key: []const u8) ![]const u8 {
@ -247,7 +251,7 @@ test "L1/L2 cache flow" {
var path_buf: [std.fs.max_path_bytes]u8 = undefined; var path_buf: [std.fs.max_path_bytes]u8 = undefined;
const cache_dir = try tmp_dir.dir.realpath(".", &path_buf); const cache_dir = try tmp_dir.dir.realpath(".", &path_buf);
var cache = try Cache.init(allocator, .{ .max_entries = 10, .cache_dir = cache_dir }); const cache = try Cache.init(allocator, .{ .max_entries = 10, .cache_dir = cache_dir });
defer cache.deinit(); defer cache.deinit();
// Put item in cache // Put item in cache
@ -271,3 +275,33 @@ test "L1/L2 cache flow" {
// Now it should be in L1 // Now it should be in L1
try std.testing.expectEqualStrings("value1", cache.lru.get("key1").?); try std.testing.expectEqualStrings("value1", cache.lru.get("key1").?);
} }
test "expired cache entry returns null" {
const allocator = std.testing.allocator;
var tmp_dir = std.testing.tmpDir(.{});
defer tmp_dir.cleanup();
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
const cache_dir = try tmp_dir.dir.realpath(".", &path_buf);
const cache = try Cache.init(allocator, .{ .max_entries = 10, .cache_dir = cache_dir });
defer cache.deinit();
// Put item with past expiration time
const now = std.time.milliTimestamp();
const past_expires = now - 1000;
// Manually insert expired entry into L1
const key_copy = try allocator.dupe(u8, "key1");
const value_copy = try allocator.dupe(u8, "value1");
try cache.lru.map.put(key_copy, .{
.value = value_copy,
.expires = past_expires,
.access_count = 0,
});
// Get should return null for expired entry
const result = cache.get("key1");
try std.testing.expect(result == null);
}

View file

@ -49,7 +49,7 @@ pub fn main() !void {
// Initialize location resolver // Initialize location resolver
var resolver = Resolver.init(allocator, &geoip, &geocache, &airports_db); var resolver = Resolver.init(allocator, &geoip, &geocache, &airports_db);
var cache = try Cache.init(allocator, .{ const cache = try Cache.init(allocator, .{
.max_entries = cfg.cache_size, .max_entries = cfg.cache_size,
.cache_dir = cfg.cache_dir, .cache_dir = cfg.cache_dir,
}); });
@ -66,7 +66,7 @@ pub fn main() !void {
defer metno.deinit(); defer metno.deinit();
var server = try Server.init(allocator, cfg.listen_host, cfg.listen_port, .{ var server = try Server.init(allocator, cfg.listen_host, cfg.listen_port, .{
.provider = metno.provider(&cache), .provider = metno.provider(cache),
.resolver = &resolver, .resolver = &resolver,
.geoip = &geoip, .geoip = &geoip,
}, &rate_limiter); }, &rate_limiter);