|
|
@@ -0,0 +1,77 @@
|
|
|
+const std = @import("std");
|
|
|
+const storage = @import("storage.zig");
|
|
|
+
|
|
|
+const MAX_PERSISTENCE_SIZE = 10_000_000 * 100;
|
|
|
+var storage_file: ?std.fs.File = null;
|
|
|
+var thread_pool: std.Thread.Pool = undefined;
|
|
|
+var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
|
+const allocator = arena.allocator();
|
|
|
+
|
|
|
+var mutex: std.Thread.Mutex = .{};
|
|
|
+
|
|
|
+pub fn init() !void {
|
|
|
+ try std.Thread.Pool.init(&thread_pool, .{ .allocator = allocator, .n_jobs = 1024 });
|
|
|
+
|
|
|
+ const cwd = std.fs.cwd();
|
|
|
+ storage_file = cwd.openFile(".db", .{ .mode = .read_write }) catch |err| {
|
|
|
+ if (err == std.fs.File.OpenError.FileNotFound) {
|
|
|
+ std.debug.print("No persisted data found, starting fresh...\n", .{});
|
|
|
+
|
|
|
+ storage_file = cwd.createFile(".db", .{}) catch |ierr| {
|
|
|
+ std.debug.print("Failed to create storage file: {any}\n", .{ierr});
|
|
|
+ return;
|
|
|
+ };
|
|
|
+
|
|
|
+ std.debug.print("Created new storage file .db\n", .{});
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ };
|
|
|
+
|
|
|
+ const storage_data = storage_file.?.readToEndAlloc(allocator, MAX_PERSISTENCE_SIZE) catch |err| {
|
|
|
+ std.debug.print("Failed to read storage file: {any}\n", .{err});
|
|
|
+ return;
|
|
|
+ };
|
|
|
+ defer allocator.free(storage_data);
|
|
|
+ var records = std.mem.splitScalar(u8, storage_data, '\r');
|
|
|
+ var record_count: usize = 0;
|
|
|
+ while (records.next()) |record| {
|
|
|
+ if (record.len == 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ record_count += 1;
|
|
|
+ std.debug.print("Restoring record N:{d}\r", .{record_count});
|
|
|
+
|
|
|
+ var kv = std.mem.splitScalar(u8, record, '|');
|
|
|
+ const key = kv.first();
|
|
|
+ const value = kv.rest();
|
|
|
+
|
|
|
+ _ = storage.writeVolatile(key, value);
|
|
|
+ }
|
|
|
+ std.debug.print("Restored {d} records from persistence", .{record_count});
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+pub fn persist(key: []const u8, value: []const u8) void {
|
|
|
+ mutex.lock();
|
|
|
+ const record = std.fmt.allocPrint(allocator, "{s}|{s}\r", .{ key, value }) catch {
|
|
|
+ std.debug.print("Failed to format record for persistence\n", .{});
|
|
|
+ mutex.unlock();
|
|
|
+ return;
|
|
|
+ };
|
|
|
+ mutex.unlock();
|
|
|
+
|
|
|
+ thread_pool.spawn(persistRecord, .{record}) catch |err| {
|
|
|
+ std.debug.print("Failed to spawn persistence job: {any}\n", .{err});
|
|
|
+ return;
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+fn persistRecord(record: []const u8) void {
|
|
|
+ mutex.lock();
|
|
|
+ defer mutex.unlock();
|
|
|
+
|
|
|
+ _ = storage_file.?.write(record) catch |err| {
|
|
|
+ std.debug.print("Failed to write record to storage file: {any}\n", .{err});
|
|
|
+ return;
|
|
|
+ };
|
|
|
+}
|