2
0

storage.zig 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. const std = @import("std");
  2. const index = @import("index.zig");
  3. const hashing = @import("hashing.zig");
  4. const persistence = @import("persistence.zig");
  5. const NUM_SHARDS = 64;
  6. const TOTAL_BUCKETS = 1_048_576;
  7. const BUCKETS_PER_SHARD = TOTAL_BUCKETS / NUM_SHARDS;
  8. const Entry = struct {
  9. key: []const u8,
  10. value: []const u8,
  11. hash: u32,
  12. next: ?*Entry,
  13. };
  14. const Shard = struct {
  15. buckets: []?*Entry,
  16. rwlock: std.Thread.RwLock,
  17. arena: std.heap.ArenaAllocator,
  18. allocator: std.mem.Allocator,
  19. };
  20. var shards: [NUM_SHARDS]Shard = undefined;
  21. var shards_initialized: bool = false;
  22. var init_mutex: std.Thread.Mutex = .{};
  23. fn getShardIndex(hash: u32) usize {
  24. return hash % NUM_SHARDS;
  25. }
  26. pub fn init() void {
  27. if (shards_initialized) return;
  28. init_mutex.lock();
  29. defer init_mutex.unlock();
  30. if (!shards_initialized) {
  31. for (&shards) |*shard| {
  32. shard.arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
  33. shard.allocator = shard.arena.allocator();
  34. shard.buckets = shard.allocator.alloc(?*Entry, BUCKETS_PER_SHARD) catch unreachable;
  35. @memset(shard.buckets, null);
  36. shard.rwlock = .{};
  37. }
  38. shards_initialized = true;
  39. }
  40. }
  41. pub fn restore(key: []const u8, value: []const u8) bool {
  42. const hash = hashing.hashKey(key);
  43. const shard_idx = getShardIndex(hash);
  44. shards[shard_idx].rwlock.lock();
  45. defer shards[shard_idx].rwlock.unlock();
  46. const entry = writeVolatile(hash, key, value);
  47. if (entry != null) {
  48. index.insert(entry.?.key);
  49. return true;
  50. }
  51. return false;
  52. }
  53. pub fn restoreDelete(key: []const u8) bool {
  54. const hash = hashing.hashKey(key);
  55. const shard_idx = getShardIndex(hash);
  56. shards[shard_idx].rwlock.lock();
  57. defer shards[shard_idx].rwlock.unlock();
  58. const deleted = deleteVolatile(hash, key);
  59. if (deleted) {
  60. index.delete(key);
  61. return true;
  62. }
  63. return false;
  64. }
  65. pub fn writeVolatile(hash: u32, key: []const u8, value: []const u8) ?*Entry {
  66. const shard_idx = getShardIndex(hash);
  67. const bucketIdx = hash % shards[shard_idx].buckets.len;
  68. const alloc = shards[shard_idx].allocator;
  69. var current = shards[shard_idx].buckets[bucketIdx];
  70. while (current) |entry| {
  71. if (entry.hash == hash and std.mem.eql(u8, entry.key, key)) {
  72. alloc.free(entry.value);
  73. entry.value = alloc.dupe(u8, value) catch return null;
  74. return entry;
  75. }
  76. current = entry.next;
  77. }
  78. const newEntry = alloc.create(Entry) catch return null;
  79. errdefer alloc.destroy(newEntry);
  80. newEntry.* = Entry{
  81. .key = alloc.dupe(u8, key) catch return null,
  82. .value = alloc.dupe(u8, value) catch return null,
  83. .hash = hash, // Cache hash value
  84. .next = shards[shard_idx].buckets[bucketIdx],
  85. };
  86. shards[shard_idx].buckets[bucketIdx] = newEntry;
  87. return newEntry;
  88. }
  89. pub fn write(key: []const u8, value: []const u8) bool {
  90. const hash = hashing.hashKey(key);
  91. const shard_idx = getShardIndex(hash);
  92. shards[shard_idx].rwlock.lock();
  93. defer shards[shard_idx].rwlock.unlock();
  94. const entry = writeVolatile(hash, key, value);
  95. if (entry == null) {
  96. return false;
  97. }
  98. index.insert(entry.?.key);
  99. persistence.persist('W', entry.?.key, entry.?.value);
  100. return true;
  101. }
  102. pub fn read(key: []const u8) ?[]const u8 {
  103. if (!shards_initialized) return null;
  104. const hash = hashing.hashKey(key);
  105. const shard_idx = getShardIndex(hash);
  106. shards[shard_idx].rwlock.lockShared();
  107. defer shards[shard_idx].rwlock.unlockShared();
  108. var current = shards[shard_idx].buckets[hash % shards[shard_idx].buckets.len];
  109. while (current) |entry| {
  110. if (entry.hash == hash and std.mem.eql(u8, entry.key, key)) {
  111. return entry.value;
  112. }
  113. current = entry.next;
  114. }
  115. return null;
  116. }
  117. pub fn deleteVolatile(hash: u32, key: []const u8) bool {
  118. if (!shards_initialized) return false;
  119. const shard_idx = getShardIndex(hash);
  120. const bucketIdx = hash % shards[shard_idx].buckets.len;
  121. const alloc = shards[shard_idx].allocator;
  122. var current = shards[shard_idx].buckets[bucketIdx];
  123. var prev: ?*Entry = null;
  124. while (current) |entry| {
  125. if (entry.hash == hash and std.mem.eql(u8, entry.key, key)) {
  126. if (prev) |p| {
  127. p.next = entry.next;
  128. } else {
  129. shards[shard_idx].buckets[bucketIdx] = entry.next;
  130. }
  131. alloc.free(entry.key);
  132. alloc.free(entry.value);
  133. alloc.destroy(entry);
  134. return true;
  135. }
  136. prev = entry;
  137. current = entry.next;
  138. }
  139. return false;
  140. }
  141. pub fn delete(key: []const u8) bool {
  142. const hash = hashing.hashKey(key);
  143. const shard_idx = getShardIndex(hash);
  144. shards[shard_idx].rwlock.lock();
  145. defer shards[shard_idx].rwlock.unlock();
  146. const deleted = deleteVolatile(hash, key);
  147. if (deleted) {
  148. index.delete(key);
  149. persistence.persist('D', key, "");
  150. }
  151. return deleted;
  152. }