@@ -9,6 +9,8 @@ const SearchResult = @import("common.zig").SearchResult;
9
9
const SearchResults = @import ("common.zig" ).SearchResults ;
10
10
const SegmentID = @import ("common.zig" ).SegmentID ;
11
11
12
+ const SegmentMergeOptions = @import ("segment_list.zig" ).SegmentMergeOptions ;
13
+
12
14
const Oplog = @import ("Oplog.zig" );
13
15
14
16
const MemorySegment = @import ("MemorySegment.zig" );
@@ -68,6 +70,11 @@ file_segment_merge_condition: std.Thread.Condition = .{},
68
70
file_segment_merge_stop : bool = false ,
69
71
file_segment_merge_thread : ? std.Thread = null ,
70
72
73
+ memory_segment_merge_mutex : std.Thread.Mutex = .{},
74
+ memory_segment_merge_condition : std.Thread.Condition = .{},
75
+ memory_segment_merge_stop : bool = false ,
76
+ memory_segment_merge_thread : ? std.Thread = null ,
77
+
71
78
pub fn init (allocator : std.mem.Allocator , dir : std.fs.Dir , options : Options ) ! Self {
72
79
var data_dir = try dir .makeOpenPath ("data" , .{ .iterate = true });
73
80
errdefer data_dir .close ();
@@ -90,6 +97,7 @@ pub fn init(allocator: std.mem.Allocator, dir: std.fs.Dir, options: Options) !Se
90
97
pub fn deinit (self : * Self ) void {
91
98
self .stopCheckpointThread ();
92
99
self .stopFileSegmentMergeThread ();
100
+ self .stopFileSegmentMergeThread ();
93
101
94
102
self .oplog .deinit ();
95
103
self .memory_segments .deinit ();
@@ -115,7 +123,11 @@ fn prepareMemorySegmentMerge(self: *Self) !?MemorySegmentList.PreparedMerge {
115
123
self .segments_lock .lockShared ();
116
124
defer self .segments_lock .unlockShared ();
117
125
118
- const merge = try self .memory_segments .prepareMerge (.{ .max_segment_size = self .options .min_segment_size }) orelse return null ;
126
+ const options = SegmentMergeOptions {
127
+ .max_segment_size = self .options .min_segment_size ,
128
+ };
129
+
130
+ const merge = try self .memory_segments .prepareMerge (options ) orelse return null ;
119
131
errdefer self .memory_segments .destroySegment (merge .target );
120
132
121
133
try merge .target .data .merge (& merge .sources .node1 .data , & merge .sources .node2 .data , & self .memory_segments );
@@ -134,6 +146,10 @@ fn finishMemorySegmentMerge(self: *Self, merge: MemorySegmentList.PreparedMerge)
134
146
135
147
self .flattenMemorySegmentIds ();
136
148
149
+ if (merge .target .data .getSize () > self .options .min_segment_size / 2 ) {
150
+ log .info ("performed big memory merge, size={}" , .{merge .target .data .getSize ()});
151
+ }
152
+
137
153
return merge .target .data .getSize () >= self .options .min_segment_size ;
138
154
}
139
155
@@ -226,9 +242,12 @@ fn loadSegment(self: *Self, segment_id: SegmentID) !void {
226
242
227
243
fn loadSegments (self : * Self ) ! void {
228
244
const segment_ids = filefmt .readIndexFile (self .data_dir , self .allocator ) catch | err | {
229
- if (err == error .FileNotFound and self .options .create ) {
230
- try filefmt .writeIndexFile (self .data_dir , &[_ ]SegmentID {});
231
- return ;
245
+ if (err == error .FileNotFound ) {
246
+ if (self .options .create ) {
247
+ try filefmt .writeIndexFile (self .data_dir , &[_ ]SegmentID {});
248
+ return ;
249
+ }
250
+ return error .IndexNotFound ;
232
251
}
233
252
return err ;
234
253
};
@@ -288,41 +307,30 @@ fn doCheckpoint(self: *Self) !bool {
288
307
}
289
308
290
309
fn checkpointThreadFn (self : * Self ) void {
291
- const min_delay = std .time .ns_per_s ;
292
- const max_delay = std .time .ns_per_s * 60 ;
293
- var delay : u64 = min_delay ;
294
- var retries : u32 = 0 ;
295
-
296
310
while (true ) {
297
311
self .checkpoint_mutex .lock ();
298
312
defer self .checkpoint_mutex .unlock ();
299
313
300
314
if (self .checkpoint_stop ) return ;
301
315
302
- var wait : bool = true ;
303
-
304
316
if (self .doCheckpoint ()) | successful | {
305
- delay = min_delay ;
306
- retries = 0 ;
307
317
if (successful ) {
308
- wait = false ;
309
318
self .file_segment_merge_condition .signal ();
319
+ continue ;
310
320
}
311
321
} else | err | {
312
- delay = @min (delay * 110 / 100 , max_delay );
313
- retries += 1 ;
314
- log .err ("checkpoint failed: {} (retry {})" , .{ err , retries });
322
+ log .err ("checkpoint failed: {}" , .{err });
315
323
}
316
324
317
- if (wait ) {
318
- self .checkpoint_condition .timedWait (& self .checkpoint_mutex , delay ) catch {};
319
- }
325
+ self .checkpoint_condition .timedWait (& self .checkpoint_mutex , std .time .ns_per_min ) catch continue ;
320
326
}
321
327
}
322
328
323
329
fn startCheckpointThread (self : * Self ) ! void {
324
330
if (self .checkpoint_thread != null ) return ;
325
331
332
+ log .info ("starting checkpoint thread" , .{});
333
+
326
334
self .checkpoint_mutex .lock ();
327
335
self .checkpoint_stop = false ;
328
336
self .checkpoint_mutex .unlock ();
@@ -344,40 +352,29 @@ fn stopCheckpointThread(self: *Self) void {
344
352
}
345
353
346
354
fn fileSegmentMergeThreadFn (self : * Self ) void {
347
- const min_delay = std .time .ns_per_s ;
348
- const max_delay = std .time .ns_per_s * 60 ;
349
- var delay : u64 = min_delay ;
350
- var retries : u32 = 0 ;
351
-
352
355
while (true ) {
353
356
self .file_segment_merge_mutex .lock ();
354
357
defer self .file_segment_merge_mutex .unlock ();
355
358
356
359
if (self .file_segment_merge_stop ) return ;
357
360
358
- var wait : bool = true ;
359
-
360
361
if (self .maybeMergeFileSegments ()) | successful | {
361
- delay = min_delay ;
362
- retries = 0 ;
363
362
if (successful ) {
364
- wait = false ;
363
+ continue ;
365
364
}
366
365
} else | err | {
367
- delay = @min (delay * 110 / 100 , max_delay );
368
- retries += 1 ;
369
- log .err ("file segment merge failed: {} (retry {})" , .{ err , retries });
366
+ log .err ("file segment merge failed: {}" , .{err });
370
367
}
371
368
372
- if (wait ) {
373
- self .checkpoint_condition .timedWait (& self .file_segment_merge_mutex , delay ) catch {};
374
- }
369
+ self .file_segment_merge_condition .timedWait (& self .file_segment_merge_mutex , std .time .ns_per_min ) catch continue ;
375
370
}
376
371
}
377
372
378
373
fn startFileSegmentMergeThread (self : * Self ) ! void {
379
374
if (self .file_segment_merge_thread != null ) return ;
380
375
376
+ log .info ("starting file segment merge thread" , .{});
377
+
381
378
self .file_segment_merge_mutex .lock ();
382
379
self .file_segment_merge_stop = false ;
383
380
self .file_segment_merge_mutex .unlock ();
@@ -402,7 +399,11 @@ fn prepareFileSegmentMerge(self: *Self) !?FileSegmentList.PreparedMerge {
402
399
self .segments_lock .lockShared ();
403
400
defer self .segments_lock .unlockShared ();
404
401
405
- const merge = try self .file_segments .prepareMerge (.{ .max_segment_size = self .options .max_segment_size }) orelse return null ;
402
+ const options = SegmentMergeOptions {
403
+ .max_segment_size = self .options .max_segment_size ,
404
+ };
405
+
406
+ const merge = try self .file_segments .prepareMerge (options ) orelse return null ;
406
407
errdefer self .file_segments .destroySegment (merge .target );
407
408
408
409
var merger = SegmentMerger (FileSegment ).init (self .allocator , & self .file_segments );
@@ -470,9 +471,55 @@ pub fn maybeMergeFileSegments(self: *Self) !bool {
470
471
return true ;
471
472
}
472
473
474
+ fn memorySegmentMergeThreadFn (self : * Self ) void {
475
+ while (true ) {
476
+ self .memory_segment_merge_mutex .lock ();
477
+ defer self .memory_segment_merge_mutex .unlock ();
478
+
479
+ if (self .memory_segment_merge_stop ) return ;
480
+
481
+ if (self .maybeMergeMemorySegments ()) | successful | {
482
+ if (successful ) {
483
+ self .checkpoint_condition .signal ();
484
+ continue ;
485
+ }
486
+ } else | err | {
487
+ log .err ("memory segment merge failed: {}" , .{err });
488
+ }
489
+
490
+ self .memory_segment_merge_condition .timedWait (& self .memory_segment_merge_mutex , std .time .ns_per_min ) catch continue ;
491
+ }
492
+ }
493
+
494
+ fn startMemorySegmentMergeThread (self : * Self ) ! void {
495
+ if (self .memory_segment_merge_thread != null ) return ;
496
+
497
+ log .info ("starting memory segment merge thread" , .{});
498
+
499
+ self .memory_segment_merge_mutex .lock ();
500
+ self .memory_segment_merge_stop = false ;
501
+ self .memory_segment_merge_mutex .unlock ();
502
+
503
+ self .memory_segment_merge_thread = try std .Thread .spawn (.{}, memorySegmentMergeThreadFn , .{self });
504
+ }
505
+
506
+ fn stopMemorySegmentMergeThread (self : * Self ) void {
507
+ self .memory_segment_merge_mutex .lock ();
508
+ self .memory_segment_merge_stop = true ;
509
+ self .memory_segment_merge_condition .broadcast ();
510
+ self .memory_segment_merge_mutex .unlock ();
511
+
512
+ if (self .memory_segment_merge_thread ) | thread | {
513
+ thread .join ();
514
+ }
515
+
516
+ self .memory_segment_merge_thread = null ;
517
+ }
518
+
473
519
pub fn open (self : * Self ) ! void {
474
520
try self .startCheckpointThread ();
475
521
try self .startFileSegmentMergeThread ();
522
+ try self .startMemorySegmentMergeThread ();
476
523
try self .loadSegments ();
477
524
try self .oplog .open (self .getMaxCommitId (), Updater { .index = self });
478
525
}
@@ -508,11 +555,14 @@ fn getFileSegmentIds(self: *Self) !std.ArrayList(SegmentID) {
508
555
}
509
556
510
557
pub fn update (self : * Self , changes : []const Change ) ! void {
511
- const start_checkpoint = try self . maybeMergeMemorySegments ();
512
- if (start_checkpoint ) {
558
+ // const t1 = std.time.milliTimestamp ();
559
+ if (try self . maybeMergeMemorySegments () ) {
513
560
self .checkpoint_condition .signal ();
514
561
}
562
+ //const t2 = std.time.milliTimestamp();
515
563
try self .oplog .write (changes , Updater { .index = self });
564
+ // const t3 = std.time.milliTimestamp();
565
+ //log.info("merge: {}ms, update: {}ms", .{ t2 - t1, t3 - t2 });
516
566
}
517
567
518
568
pub fn search (self : * Self , hashes : []const u32 , allocator : std.mem.Allocator , deadline : Deadline ) ! SearchResults {
0 commit comments