persistence.zig 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. const std = @import("std");
  2. const storage = @import("storage.zig");
  3. const MAX_PERSISTENCE_SIZE = 10_000_000 * 100;
  4. var storage_file: ?std.fs.File = null;
  5. var thread_pool: std.Thread.Pool = undefined;
  6. var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
  7. const allocator = arena.allocator();
  8. var mutex: std.Thread.Mutex = .{};
  9. const OPCode = enum {
  10. W,
  11. D,
  12. };
  13. pub fn init() !void {
  14. try std.Thread.Pool.init(&thread_pool, .{ .allocator = allocator, .n_jobs = 1024 });
  15. const cwd = std.fs.cwd();
  16. storage_file = cwd.openFile(".db", .{ .mode = .read_write }) catch |err| {
  17. if (err == std.fs.File.OpenError.FileNotFound) {
  18. std.debug.print("No persisted data found, starting fresh...\n", .{});
  19. storage_file = cwd.createFile(".db", .{}) catch |ierr| {
  20. std.debug.print("Failed to create storage file: {any}\n", .{ierr});
  21. return;
  22. };
  23. std.debug.print("Created new storage file .db\n", .{});
  24. }
  25. return;
  26. };
  27. const storage_data = storage_file.?.readToEndAlloc(allocator, MAX_PERSISTENCE_SIZE) catch |err| {
  28. std.debug.print("Failed to read storage file: {any}\n", .{err});
  29. return;
  30. };
  31. defer allocator.free(storage_data);
  32. var records = std.mem.splitScalar(u8, storage_data, '\r');
  33. var record_count: usize = 0;
  34. while (records.next()) |record| {
  35. if (record.len == 0) {
  36. continue;
  37. }
  38. record_count += 1;
  39. std.debug.print("Restoring record N:{d}\r", .{record_count});
  40. var recordIterator = std.mem.splitScalar(u8, record, '|');
  41. const opcode = recordIterator.first();
  42. const key = recordIterator.next() orelse continue;
  43. const value = recordIterator.next() orelse continue;
  44. const opcodeEnum = std.meta.stringToEnum(OPCode, opcode) orelse continue;
  45. switch (opcodeEnum) {
  46. .W => _ = storage.writeVolatile(key, value),
  47. .D => _ = storage.deleteVolatile(key),
  48. }
  49. }
  50. std.debug.print("Restored {d} records from persistence", .{record_count});
  51. return;
  52. }
  53. pub fn persist(opcode: u8, key: []const u8, value: []const u8) void {
  54. mutex.lock();
  55. const record = std.fmt.allocPrint(allocator, "{c}|{s}|{s}\r", .{ opcode, key, value }) catch {
  56. std.debug.print("Failed to format record for persistence\n", .{});
  57. mutex.unlock();
  58. return;
  59. };
  60. mutex.unlock();
  61. thread_pool.spawn(persistRecord, .{record}) catch |err| {
  62. std.debug.print("Failed to spawn persistence job: {any}\n", .{err});
  63. return;
  64. };
  65. }
  66. fn persistRecord(record: []const u8) void {
  67. mutex.lock();
  68. defer mutex.unlock();
  69. _ = storage_file.?.write(record) catch |err| {
  70. std.debug.print("Failed to write record to storage file: {any}\n", .{err});
  71. return;
  72. };
  73. }