Skip to content

Commit f7a34ed

Browse files
authored
Compactor workshop viable impl (#13)
* add base compactor * fix deletion for multiple segments * ahead * finish compactor
1 parent 9fa9be6 commit f7a34ed

File tree

7 files changed

+116
-12
lines changed

7 files changed

+116
-12
lines changed

lib/amnesia.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
require_relative 'amnesia/cli'
2+
require_relative 'amnesia/compactor'
23

34
dir = './_data'
45

56
segment_files = Dir.each_child(dir).map { |f| "#{dir}/#{f}" }.sort_by { |f| File::Stat.new(f).mtime }
67

8+
# Amnesia::Compactor.call(*segment_files.take(2))
9+
710
Amnesia::CLI.new(segment_files, populate_index: true).start

lib/amnesia/cli.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ def initialize(segment_files, populate_index: nil)
1919
end
2020

2121
def start
22+
segment_handler.compact
23+
2224
puts "Welcome to AmnesiaDB - Version 0.1.0\n\n"
2325

2426
loop do

lib/amnesia/compactor.rb

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
module Amnesia
2+
class Compactor
3+
def self.call(sstable1, sstable2)
4+
# todos itens de sstable1 e sstable2
5+
sstable_items1 = sstable1.all
6+
sstable_items2 = sstable2.all
7+
8+
pp sstable_items1
9+
pp sstable_items2
10+
11+
puts ''
12+
13+
# ponteiro 1, ponteiro 2 -> 0
14+
p1 = p2 = 0
15+
16+
result = []
17+
18+
# loop ate que eu termine os registros de ambos os arquivos
19+
loop do
20+
item1 = sstable_items1[p1] # item é uma tupla ['name', 'mateus']
21+
item2 = sstable_items2[p2]
22+
23+
break if item1.nil? && item2.nil?
24+
25+
# TODO: o que acontece se uma das keys for nil ?
26+
key1 = item1[0] unless item1.nil?
27+
key2 = item2[0] unless item2.nil?
28+
29+
min_key = [key1, key2].compact.min
30+
31+
if key1 == key2
32+
p1 += 1
33+
p2 += 1
34+
35+
result << item1
36+
37+
next
38+
end
39+
40+
if min_key == key1
41+
p1 += 1
42+
43+
result << item1
44+
else
45+
p2 += 1
46+
47+
result << item2
48+
end
49+
end
50+
51+
pp result
52+
53+
result.reject { |(_key, value)| value.nil? || value.empty? }
54+
end
55+
end
56+
end

lib/amnesia/memtable_handler.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module Amnesia
22
class MemtableHandler
3-
def initialize(segment_handler, max_items_threshold = 5)
3+
def initialize(segment_handler, max_items_threshold = 2)
44
@segment_handler = segment_handler
55
@max_items_threshold = max_items_threshold
66

lib/amnesia/segment.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ module Amnesia
22
class Segment
33
attr_reader :index_structure
44

5-
def initialize(filename, index_structure: Amnesia::Indexes::HashIndex.new)
5+
def initialize(filename, items: nil, index_structure: Amnesia::Indexes::HashIndex.new)
66
@filename = filename
7-
@storage = Amnesia::Storage.new(filename)
7+
@storage = Amnesia::Storage.new(filename, items: items)
88
@storage.create_db_file unless @storage.file_exists?
99
@index_structure = index_structure
1010
end
@@ -13,6 +13,14 @@ def name
1313
@filename
1414
end
1515

16+
def destroy
17+
File.delete(@filename)
18+
end
19+
20+
def all
21+
@storage.all
22+
end
23+
1624
def retrieve(key)
1725
index_entry = @index_structure.get(key)
1826

lib/amnesia/segment_handler.rb

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,28 @@ def current_segment
99
@segments.first
1010
end
1111

12+
def segments
13+
@segments ||= []
14+
end
15+
16+
def compact
17+
segments_to_compact = segments[-2..]
18+
19+
return if segments_to_compact.nil?
20+
21+
pp segments_to_compact
22+
23+
filename = "./_data/#{Time.now.to_i}.c.segment"
24+
25+
compaction_result = Amnesia::Compactor.call(*segments_to_compact)
26+
27+
new_segment = Amnesia::Segment.new(filename, items: compaction_result)
28+
29+
@segments.unshift(new_segment)
30+
31+
segments.pop(2).each(&:destroy)
32+
end
33+
1234
def flush(items)
1335
# TODO: Use the storage class for that
1436
filename = "./_data/#{Time.now.to_i}.segment"
@@ -17,7 +39,9 @@ def flush(items)
1739
items.each { |(key, value)| f.write("#{key},#{value}\n") }
1840
end
1941

20-
@segments << Amnesia::Segment.new(filename)
42+
@segments.unshift(Amnesia::Segment.new(filename))
43+
44+
compact if @segments.length == 2
2145

2246
:finished_flushing
2347
end

lib/amnesia/storage.rb

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ module Amnesia
22
class Storage
33
attr_reader :filename
44

5-
def initialize(filename)
5+
def initialize(filename, items: nil)
66
@filename = filename
7+
populate_data(items) unless items.nil? || items.empty?
78
end
89

910
def size
@@ -16,6 +17,14 @@ def set(key, value)
1617
File.write(filename, entry, mode: 'a+')
1718
end
1819

20+
def all
21+
File.readlines(filename).map do |record|
22+
key, value = record.chomp.split(',')
23+
24+
[key, value]
25+
end
26+
end
27+
1928
def delete(key)
2029
set(key, '')
2130
end
@@ -27,19 +36,15 @@ def get(key, index_entry: nil)
2736
end
2837

2938
def parse_record(raw_record)
30-
value = raw_record_value(raw_record)
31-
32-
return nil if value.nil? || value.empty?
33-
34-
value
39+
raw_record_value(raw_record)
3540
end
3641

3742
def raw_record_value(raw_record)
3843
(raw_record || '').chomp.split(',', 2)[1]
3944
end
4045

41-
def create_db_file
42-
File.write(filename, '') unless file_exists?
46+
def create_db_file(content = '')
47+
File.write(filename, content) unless file_exists?
4348
end
4449

4550
def file_exists?
@@ -48,6 +53,12 @@ def file_exists?
4853

4954
private
5055

56+
def populate_data(items)
57+
data_block = items.map { |(key, value)| "#{key},#{value}\n" }.join('')
58+
59+
create_db_file(data_block)
60+
end
61+
5162
def record_from_scan(key)
5263
lines = File.readlines(filename)
5364

0 commit comments

Comments
 (0)