strokedb 0.0.2.1 → 0.0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +18 -20
- data/bench.html +4001 -0
- data/bin/strokedb +14 -0
- data/examples/movies.rb +105 -0
- data/examples/movies2.rb +97 -0
- data/examples/strokewiki/README +28 -0
- data/examples/strokewiki/view/edit.xhtml +27 -0
- data/examples/strokewiki/view/new.xhtml +26 -0
- data/examples/strokewiki/view/pages.xhtml +27 -0
- data/examples/strokewiki/view/show.xhtml +40 -0
- data/examples/strokewiki/view/versions.xhtml +25 -0
- data/examples/strokewiki/wiki.rb +106 -0
- data/examples/todo.rb +92 -0
- data/lib/strokedb.rb +85 -0
- data/lib/{config → strokedb}/config.rb +14 -9
- data/lib/strokedb/console.rb +87 -0
- data/lib/strokedb/core_ext.rb +10 -0
- data/lib/{util/ext → strokedb/core_ext}/blank.rb +1 -1
- data/lib/{util/ext → strokedb/core_ext}/enumerable.rb +0 -0
- data/lib/{util/ext → strokedb/core_ext}/fixnum.rb +0 -0
- data/lib/strokedb/core_ext/float.rb +4 -0
- data/lib/{util/ext → strokedb/core_ext}/hash.rb +0 -0
- data/lib/strokedb/core_ext/infinity.rb +33 -0
- data/lib/strokedb/core_ext/kernel.rb +41 -0
- data/lib/strokedb/core_ext/object.rb +16 -0
- data/lib/{util/ext → strokedb/core_ext}/string.rb +28 -1
- data/lib/strokedb/core_ext/symbol.rb +13 -0
- data/lib/strokedb/data_structures.rb +5 -0
- data/lib/strokedb/data_structures/chunked_skiplist.rb +123 -0
- data/lib/{data_structures → strokedb/data_structures}/inverted_list.rb +0 -0
- data/lib/{data_structures → strokedb/data_structures}/point_query.rb +0 -0
- data/lib/strokedb/data_structures/simple_skiplist.rb +350 -0
- data/lib/{data_structures → strokedb/data_structures}/skiplist.rb +1 -1
- data/lib/{document → strokedb}/document.rb +180 -71
- data/lib/{document → strokedb/document}/callback.rb +0 -0
- data/lib/{document → strokedb/document}/delete.rb +2 -2
- data/lib/strokedb/document/dsl.rb +4 -0
- data/lib/{document → strokedb/document/dsl}/associations.rb +0 -0
- data/lib/{document → strokedb/document/dsl}/coercions.rb +0 -0
- data/lib/strokedb/document/dsl/meta_dsl.rb +7 -0
- data/lib/{document → strokedb/document/dsl}/validations.rb +26 -21
- data/lib/{document → strokedb/document/dsl}/virtualize.rb +0 -0
- data/lib/{document → strokedb/document}/meta.rb +92 -29
- data/lib/{document → strokedb/document}/slot.rb +17 -5
- data/lib/{document → strokedb/document}/util.rb +0 -0
- data/lib/{document → strokedb/document}/versions.rb +2 -2
- data/lib/strokedb/index.rb +2 -0
- data/lib/strokedb/nsurl.rb +24 -0
- data/lib/strokedb/store.rb +149 -0
- data/lib/strokedb/stores.rb +6 -0
- data/lib/{stores → strokedb/stores}/chainable_storage.rb +20 -14
- data/lib/strokedb/stores/file_storage.rb +118 -0
- data/lib/{stores/inverted_list_index → strokedb/stores}/inverted_list_file_storage.rb +50 -0
- data/lib/strokedb/stores/memory_storage.rb +80 -0
- data/lib/{stores → strokedb/stores}/remote_store.rb +10 -4
- data/lib/strokedb/sync.rb +4 -0
- data/lib/{sync → strokedb/sync}/chain_sync.rb +0 -0
- data/lib/{sync → strokedb/sync}/diff.rb +12 -1
- data/lib/{sync/stroke_diff → strokedb/sync/diff}/array.rb +1 -1
- data/lib/{sync/stroke_diff → strokedb/sync/diff}/default.rb +0 -0
- data/lib/{sync/stroke_diff → strokedb/sync/diff}/hash.rb +1 -1
- data/lib/{sync/stroke_diff → strokedb/sync/diff}/string.rb +1 -1
- data/lib/{sync → strokedb/sync}/lamport_timestamp.rb +0 -0
- data/lib/{sync → strokedb/sync}/store_sync.rb +15 -7
- data/lib/strokedb/transaction.rb +78 -0
- data/lib/{util → strokedb}/util.rb +14 -7
- data/lib/strokedb/util/attach_dsl.rb +29 -0
- data/lib/{util → strokedb/util}/blankslate.rb +0 -0
- data/lib/strokedb/util/class_optimization.rb +93 -0
- data/lib/{util → strokedb/util}/inflect.rb +0 -0
- data/lib/strokedb/util/java_util.rb +13 -0
- data/lib/{util → strokedb/util}/lazy_array.rb +0 -0
- data/lib/{util → strokedb/util}/lazy_mapping_array.rb +4 -0
- data/lib/{util → strokedb/util}/lazy_mapping_hash.rb +0 -0
- data/lib/{util → strokedb/util}/serialization.rb +21 -0
- data/lib/strokedb/util/uuid.rb +159 -0
- data/lib/{util → strokedb/util}/xml.rb +0 -0
- data/lib/{view → strokedb}/view.rb +2 -2
- data/lib/strokedb/volumes.rb +5 -0
- data/lib/strokedb/volumes/archive_volume.rb +165 -0
- data/lib/strokedb/volumes/block_volume.rb +169 -0
- data/lib/strokedb/volumes/distributed_pointer.rb +43 -0
- data/lib/strokedb/volumes/fixed_length_skiplist_volume.rb +109 -0
- data/lib/strokedb/volumes/map_volume.rb +268 -0
- data/meta/MANIFEST +175 -0
- data/script/console +2 -70
- data/spec/integration/remote_store_spec.rb +70 -0
- data/spec/integration/search_spec.rb +76 -0
- data/spec/integration/spec_helper.rb +1 -0
- data/spec/lib/spec_helper.rb +1 -0
- data/spec/lib/strokedb/config_spec.rb +250 -0
- data/spec/lib/strokedb/core_ext/blank_spec.rb +20 -0
- data/spec/lib/strokedb/core_ext/extract_spec.rb +42 -0
- data/spec/lib/strokedb/core_ext/float_spec.rb +62 -0
- data/spec/lib/strokedb/core_ext/infinity_spec.rb +40 -0
- data/spec/lib/strokedb/core_ext/spec_helper.rb +1 -0
- data/spec/lib/strokedb/core_ext/string_spec.rb +25 -0
- data/spec/lib/strokedb/core_ext/symbol_spec.rb +8 -0
- data/spec/lib/strokedb/data_structures/chunked_skiplist_spec.rb +144 -0
- data/spec/lib/strokedb/data_structures/inverted_list_spec.rb +172 -0
- data/spec/lib/strokedb/data_structures/simple_skiplist_spec.rb +200 -0
- data/spec/lib/strokedb/data_structures/skiplist_spec.rb +253 -0
- data/spec/lib/strokedb/data_structures/spec_helper.rb +1 -0
- data/spec/lib/strokedb/document/associations_spec.rb +319 -0
- data/spec/lib/strokedb/document/callbacks_spec.rb +134 -0
- data/spec/lib/strokedb/document/coercions_spec.rb +110 -0
- data/spec/lib/strokedb/document/document_spec.rb +1063 -0
- data/spec/lib/strokedb/document/meta_meta_spec.rb +30 -0
- data/spec/lib/strokedb/document/meta_spec.rb +435 -0
- data/spec/lib/strokedb/document/metaslot_spec.rb +43 -0
- data/spec/lib/strokedb/document/slot_spec.rb +130 -0
- data/spec/lib/strokedb/document/spec_helper.rb +1 -0
- data/spec/lib/strokedb/document/validations_spec.rb +1081 -0
- data/spec/lib/strokedb/document/virtualize_spec.rb +80 -0
- data/spec/lib/strokedb/nsurl_spec.rb +73 -0
- data/spec/lib/strokedb/spec_helper.rb +1 -0
- data/spec/lib/strokedb/stores/chained_storages_spec.rb +116 -0
- data/spec/lib/strokedb/stores/spec_helper.rb +1 -0
- data/spec/lib/strokedb/stores/store_spec.rb +201 -0
- data/spec/lib/strokedb/stores/transaction_spec.rb +107 -0
- data/spec/lib/strokedb/sync/chain_sync_spec.rb +43 -0
- data/spec/lib/strokedb/sync/diff_spec.rb +111 -0
- data/spec/lib/strokedb/sync/lamport_timestamp_spec.rb +174 -0
- data/spec/lib/strokedb/sync/slot_diff_spec.rb +164 -0
- data/spec/lib/strokedb/sync/spec_helper.rb +1 -0
- data/spec/lib/strokedb/sync/store_sync_spec.rb +181 -0
- data/spec/lib/strokedb/sync/stroke_diff/array_spec.rb +97 -0
- data/spec/lib/strokedb/sync/stroke_diff/complex_spec.rb +58 -0
- data/spec/lib/strokedb/sync/stroke_diff/hash_spec.rb +144 -0
- data/spec/lib/strokedb/sync/stroke_diff/scalar_spec.rb +23 -0
- data/spec/lib/strokedb/sync/stroke_diff/spec_helper.rb +25 -0
- data/spec/lib/strokedb/sync/stroke_diff/string_spec.rb +61 -0
- data/spec/lib/strokedb/util/attach_dsl_spec.rb +45 -0
- data/spec/lib/strokedb/util/inflect_spec.rb +14 -0
- data/spec/lib/strokedb/util/lazy_array_spec.rb +157 -0
- data/spec/lib/strokedb/util/lazy_mapping_array_spec.rb +174 -0
- data/spec/lib/strokedb/util/lazy_mapping_hash_spec.rb +92 -0
- data/spec/lib/strokedb/util/spec_helper.rb +1 -0
- data/spec/lib/strokedb/util/uuid_spec.rb +46 -0
- data/spec/lib/strokedb/view_spec.rb +228 -0
- data/spec/lib/strokedb/volumes/archive_volume_spec.rb +105 -0
- data/spec/lib/strokedb/volumes/block_volume_spec.rb +100 -0
- data/spec/lib/strokedb/volumes/distributed_pointer_spec.rb +14 -0
- data/spec/lib/strokedb/volumes/fixed_length_skiplist_volume_spec.rb +177 -0
- data/spec/lib/strokedb/volumes/map_volume_spec.rb +172 -0
- data/spec/lib/strokedb/volumes/spec_helper.rb +1 -0
- data/spec/regression/docref_spec.rb +94 -0
- data/spec/regression/meta_spec.rb +23 -0
- data/spec/regression/spec_helper.rb +1 -0
- data/spec/regression/sync_spec.rb +36 -0
- data/spec/spec.opts +7 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/temp/storages/TIMESTAMP +1 -0
- data/spec/temp/storages/UUID +1 -0
- data/spec/temp/storages/database-sync/TIMESTAMP +1 -0
- data/spec/temp/storages/database-sync/UUID +1 -0
- data/spec/temp/storages/database-sync/config +1 -0
- data/spec/temp/storages/database-sync/file/LAST +1 -0
- data/spec/temp/storages/database-sync/file/bd/f6/bdf675e5-8a7b-494e-97f2-f74a14ccd95d.av +0 -0
- data/spec/temp/storages/database-sync/file/uindex.wal +0 -0
- data/spec/temp/storages/database-sync/inverted_list_file/INVERTED_INDEX +1 -0
- data/spec/temp/storages/inverted_list_storage/INVERTED_INDEX +0 -0
- data/strokedb.gemspec +120 -0
- data/task/benchmark.task +9 -0
- data/task/ditz.task +30 -0
- data/task/echoe.rb +17 -0
- data/task/rcov.task +50 -0
- data/task/rdoc.task +10 -0
- data/task/rspec.task +0 -0
- data/vendor/java_inline.rb +106 -0
- data/vendor/rbmodexcl/mrimodexcl.rb +82 -0
- data/vendor/rbmodexcl/rbmodexcl.rb +5 -0
- data/vendor/rbmodexcl/rbxmodexcl.rb +48 -0
- data/vendor/rbmodexcl/spec/unextend_spec.rb +50 -0
- data/vendor/rbmodexcl/spec/uninclude_spec.rb +26 -0
- metadata +271 -79
- data/CONTRIBUTORS +0 -7
- data/CREDITS +0 -13
- data/bin/sdbc +0 -2
- data/lib/init.rb +0 -57
- data/lib/stores/inverted_list_index/inverted_list_index.rb +0 -49
- data/lib/stores/skiplist_store/chunk.rb +0 -119
- data/lib/stores/skiplist_store/chunk_storage.rb +0 -21
- data/lib/stores/skiplist_store/file_chunk_storage.rb +0 -44
- data/lib/stores/skiplist_store/memory_chunk_storage.rb +0 -37
- data/lib/stores/skiplist_store/skiplist_store.rb +0 -217
- data/lib/stores/store.rb +0 -5
- data/lib/sync/stroke_diff/stroke_diff.rb +0 -9
- data/lib/util/ext/object.rb +0 -8
- data/lib/util/java_util.rb +0 -9
- data/lib/util/trigger_partition.rb +0 -136
- data/strokedb.rb +0 -75
data/CONTRIBUTORS
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
Adrian Madrid <aemadrid@gmail.com>
|
|
2
|
-
Aman Gupta <aman@tmm1.net>
|
|
3
|
-
Claudio Perez Gamayo <crossblaim@gmail.com>
|
|
4
|
-
elliottcable.name <strokedb@elliottcable.com>
|
|
5
|
-
Joshua Miller <elefantstn@gmail.com>
|
|
6
|
-
Oleg Dashevskii <be9@be9.ru>
|
|
7
|
-
Michael Klishin (novemberain.com) <michael.s.klishin@gmail.com>
|
data/CREDITS
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
MANY THANKS TO
|
|
3
|
-
(for knowledge and aspiration)
|
|
4
|
-
|
|
5
|
-
1) Andrew S. Tanenbaum, for book "Distributed Systems".
|
|
6
|
-
2) William Pugh for skiplists.
|
|
7
|
-
3) Xin Dong & Alon Halevy for the great article "Indexing Dataspaces".
|
|
8
|
-
4) Linus Torvalds & Junio C. Hamano for the Git version control system.
|
|
9
|
-
5) Damien Katz for the CouchDB.
|
|
10
|
-
6) Yukihiro Matsumoto for the Ruby programming language.
|
|
11
|
-
7) Dr. Leslie Lamport for timestamps and signatures.
|
|
12
|
-
8) Victor Sovetov for years of talking with Yurii about databases and metaframes.
|
|
13
|
-
9) Konstantin Olenin for talks with Oleg about distributed systems and algorithms.
|
data/bin/sdbc
DELETED
data/lib/init.rb
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
module StrokeDB
|
|
2
|
-
VERSION = '0.0.2.1' + (RUBY_PLATFORM =~ /java/ ? '-java' : '')
|
|
3
|
-
|
|
4
|
-
# UUID regexp (like 1e3d02cc-0769-4bd8-9113-e033b246b013)
|
|
5
|
-
UUID_RE = /([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/
|
|
6
|
-
|
|
7
|
-
# document version regexp
|
|
8
|
-
VERSION_RE = UUID_RE
|
|
9
|
-
|
|
10
|
-
# following are special UUIDs used by StrokeDB
|
|
11
|
-
|
|
12
|
-
# so called Nil UUID, should be used as special UUID for Meta meta
|
|
13
|
-
NIL_UUID = "00000000-0000-0000-0000-000000000000"
|
|
14
|
-
|
|
15
|
-
# UUID used for DeletedDocument meta
|
|
16
|
-
DELETED_DOCUMENT_UUID = 'e5e0ef20-e10f-4269-bff3-3040a90e194e'
|
|
17
|
-
|
|
18
|
-
# UUID used for StoreInfo meta
|
|
19
|
-
STORE_INFO_UUID = "23e11d2e-e3d3-4c24-afd2-b3316403dd03"
|
|
20
|
-
|
|
21
|
-
# UUID used for Diff meta
|
|
22
|
-
DIFF_UUID = "5704bd39-4a01-405e-bc72-3650ddd89ca4"
|
|
23
|
-
|
|
24
|
-
# UUID used for SynchronizationReport meta
|
|
25
|
-
SYNCHRONIZATION_REPORT_UUID = "8dbaf160-addd-401a-9c29-06b03f70df93"
|
|
26
|
-
|
|
27
|
-
# UUID used for SynchronizationConflict meta
|
|
28
|
-
SYNCHRONIZATION_CONFLICT_UUID = "36fce59c-ee3d-4566-969b-7b152814a314"
|
|
29
|
-
|
|
30
|
-
# UUID used for View meta
|
|
31
|
-
VIEW_UUID = "ced0ad12-7419-4db1-a9f4-bc35e9b64112"
|
|
32
|
-
|
|
33
|
-
# UUID used for ViewCut meta
|
|
34
|
-
VIEWCUT_UUID = "2975630e-c877-4eab-b86c-732e1de1adf5"
|
|
35
|
-
|
|
36
|
-
class <<self
|
|
37
|
-
def default_store
|
|
38
|
-
StrokeDB.default_config.stores[:default] rescue nil
|
|
39
|
-
end
|
|
40
|
-
def default_store=(store)
|
|
41
|
-
cfg = Config.new
|
|
42
|
-
cfg.stores[:default] = store
|
|
43
|
-
StrokeDB.default_config = cfg
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
if ENV['DEBUG'] || $DEBUG
|
|
48
|
-
def DEBUG
|
|
49
|
-
yield
|
|
50
|
-
end
|
|
51
|
-
else
|
|
52
|
-
def DEBUG
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
class NoDefaultStoreError < Exception ; end
|
|
57
|
-
end
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
module StrokeDB
|
|
2
|
-
class InvertedListIndex
|
|
3
|
-
attr_accessor :storage, :document_store
|
|
4
|
-
def initialize(storage)
|
|
5
|
-
@storage = storage
|
|
6
|
-
@list = nil
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def find_uuids(*args)
|
|
10
|
-
list.find(*args)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def find(*args)
|
|
14
|
-
find_uuids(*args).map do |uuid|
|
|
15
|
-
@document_store.find(uuid)
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def insert(doc)
|
|
20
|
-
slots = indexable_slots_for_doc(doc)
|
|
21
|
-
q = PointQuery.new(slots)
|
|
22
|
-
list.insert(q.slots, doc.uuid)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def delete(doc)
|
|
26
|
-
slots = indexable_slots_for_doc(doc)
|
|
27
|
-
q = PointQuery.new(slots)
|
|
28
|
-
list.delete(q.slots, doc.uuid)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def save!
|
|
32
|
-
@storage.save!(list)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
private
|
|
36
|
-
|
|
37
|
-
def indexable_slots_for_doc(doc)
|
|
38
|
-
raw_slots = doc.to_raw
|
|
39
|
-
nkeys = doc.meta['non_indexable_slots']
|
|
40
|
-
nkeys.each{|nk| raw_slots.delete(nk) } if nkeys
|
|
41
|
-
raw_slots
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def list
|
|
45
|
-
@list ||= @storage.find_list
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
end
|
|
49
|
-
end
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
module StrokeDB
|
|
2
|
-
class Chunk
|
|
3
|
-
attr_accessor :skiplist, :next_chunk, :prev_chunk, :uuid, :cut_level, :timestamp
|
|
4
|
-
attr_accessor :next_chunk_uuid
|
|
5
|
-
attr_accessor :store_uuid
|
|
6
|
-
def initialize(cut_level)
|
|
7
|
-
@skiplist, @cut_level = Skiplist.new({}, nil, cut_level), cut_level
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def insert(uuid, raw_doc, __cheaters_level = nil, __timestamp = nil)
|
|
11
|
-
@uuid ||= uuid
|
|
12
|
-
__cheaters_level ||= $DEBUG_CHEATERS_LEVEL
|
|
13
|
-
a, new_list = skiplist.insert(uuid, raw_doc, __cheaters_level, __timestamp)
|
|
14
|
-
if new_list
|
|
15
|
-
tmp = Chunk.new(@cut_level)
|
|
16
|
-
tmp.skiplist = new_list
|
|
17
|
-
tmp.next_chunk = @next_chunk if @next_chunk
|
|
18
|
-
@next_chunk = tmp
|
|
19
|
-
@next_chunk.uuid = uuid
|
|
20
|
-
end
|
|
21
|
-
[self, @next_chunk]
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def delete(uuid)
|
|
25
|
-
skiplist.delete(uuid)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def find(uuid, default = nil)
|
|
29
|
-
skiplist.find(uuid, default)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def find_node(uuid)
|
|
33
|
-
skiplist.find_node(uuid)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def find_nearest(uuid, default = nil)
|
|
37
|
-
skiplist.find_nearest(uuid, default)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Finds next node across separate chunks
|
|
41
|
-
def find_next_node(node)
|
|
42
|
-
chunk = self
|
|
43
|
-
node2 = node.next
|
|
44
|
-
if node2.is_a?(Skiplist::TailNode)
|
|
45
|
-
chunk = chunk.next_chunk
|
|
46
|
-
unless chunk.nil?
|
|
47
|
-
node2 = chunk.first_node
|
|
48
|
-
else
|
|
49
|
-
node2 = nil
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
node2
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def first_uuid
|
|
57
|
-
skiplist.first_node.key
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def first_node
|
|
61
|
-
skiplist.first_node
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def size
|
|
65
|
-
skiplist.size
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def each(&block)
|
|
69
|
-
skiplist.each &block
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Raw format
|
|
73
|
-
|
|
74
|
-
# TODO: lazify
|
|
75
|
-
def self.from_raw(raw)
|
|
76
|
-
chunk = Chunk.new(raw['cut_level'])
|
|
77
|
-
chunk.uuid = raw['uuid']
|
|
78
|
-
chunk.next_chunk_uuid = raw['next_uuid']
|
|
79
|
-
chunk.timestamp = raw['timestamp']
|
|
80
|
-
chunk.store_uuid = raw['store_uuid']
|
|
81
|
-
chunk.skiplist.raw_insert(raw['nodes']) do |rn|
|
|
82
|
-
[rn['key'], rn['value'], rn['forward'].size, rn['timestamp']]
|
|
83
|
-
end
|
|
84
|
-
yield(chunk) if block_given?
|
|
85
|
-
chunk
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def to_raw
|
|
89
|
-
# enumerate nodes
|
|
90
|
-
skiplist.each_with_index do |node, i|
|
|
91
|
-
node._serialized_index = i
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# now we know keys' positions right in the nodes
|
|
95
|
-
nodes = skiplist.map do |node|
|
|
96
|
-
{
|
|
97
|
-
'key' => node.key,
|
|
98
|
-
'forward' => node.forward.map{|n| n._serialized_index || 0 },
|
|
99
|
-
'value' => node.value,
|
|
100
|
-
'timestamp' => node.timestamp
|
|
101
|
-
}
|
|
102
|
-
end
|
|
103
|
-
{
|
|
104
|
-
'nodes' => nodes,
|
|
105
|
-
'cut_level' => @cut_level,
|
|
106
|
-
'uuid' => @uuid,
|
|
107
|
-
# TODO: may not be needed
|
|
108
|
-
'next_uuid' => next_chunk ? next_chunk.uuid : nil,
|
|
109
|
-
'timestamp' => @timestamp,
|
|
110
|
-
'store_uuid' => @store_uuid
|
|
111
|
-
}
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def eql?(chunk)
|
|
115
|
-
chunk.uuid == @uuid && chunk.skiplist.eql?(@skiplist)
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
end
|
|
119
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
module StrokeDB
|
|
2
|
-
class ChunkStorage
|
|
3
|
-
include ChainableStorage
|
|
4
|
-
|
|
5
|
-
attr_accessor :authoritative_source
|
|
6
|
-
|
|
7
|
-
def initialize(opts={})
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def find(uuid)
|
|
11
|
-
unless result = read(chunk_path(uuid))
|
|
12
|
-
if authoritative_source
|
|
13
|
-
result = authoritative_source.find(uuid)
|
|
14
|
-
save!(result, authoritative_source) if result
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
result
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
end
|
|
21
|
-
end
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
module StrokeDB
|
|
2
|
-
class FileChunkStorage < ChunkStorage
|
|
3
|
-
attr_accessor :path
|
|
4
|
-
|
|
5
|
-
def initialize(opts={})
|
|
6
|
-
opts = opts.stringify_keys
|
|
7
|
-
@path = opts['path']
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def delete!(chunk_uuid)
|
|
11
|
-
FileUtils.rm_rf(chunk_path(chunk_uuid))
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def clear!
|
|
15
|
-
FileUtils.rm_rf @path
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
private
|
|
19
|
-
|
|
20
|
-
def perform_save!(chunk)
|
|
21
|
-
FileUtils.mkdir_p @path
|
|
22
|
-
write(chunk_path(chunk.uuid), chunk)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def read(path)
|
|
26
|
-
return nil unless File.exist?(path)
|
|
27
|
-
raw_chunk = StrokeDB.deserialize(IO.read(path))
|
|
28
|
-
Chunk.from_raw(raw_chunk) do |chunk|
|
|
29
|
-
chunk.next_chunk = find(chunk.next_chunk_uuid) if chunk.next_chunk_uuid
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def write(path, chunk)
|
|
34
|
-
File.open path, "w+" do |f|
|
|
35
|
-
f.write StrokeDB.serialize(chunk.to_raw)
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def chunk_path(uuid)
|
|
40
|
-
"#{@path}/#{uuid}"
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
end
|
|
44
|
-
end
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
module StrokeDB
|
|
2
|
-
class MemoryChunkStorage < ChunkStorage
|
|
3
|
-
attr_accessor :chunks_cache
|
|
4
|
-
|
|
5
|
-
def initialize(opts={})
|
|
6
|
-
@chunks_cache = {}
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def delete!(chunk_uuid)
|
|
10
|
-
write(chunk_path(chunk_uuid), nil)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def clear!
|
|
14
|
-
@chunks_cache.clear
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
private
|
|
18
|
-
|
|
19
|
-
def perform_save!(chunk)
|
|
20
|
-
write(chunk_path(chunk.uuid), chunk)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def read(path)
|
|
24
|
-
@chunks_cache[path]
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def write(path, chunk)
|
|
28
|
-
@chunks_cache[path] = chunk
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def chunk_path(uuid)
|
|
32
|
-
uuid
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
end
|
|
37
|
-
end
|
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
module StrokeDB
|
|
2
|
-
class SkiplistStore < Store
|
|
3
|
-
include Enumerable
|
|
4
|
-
attr_accessor :chunk_storage, :cut_level, :index_store
|
|
5
|
-
|
|
6
|
-
def initialize(opts={})
|
|
7
|
-
opts = opts.stringify_keys
|
|
8
|
-
@chunk_storage = opts['storage']
|
|
9
|
-
@cut_level = opts['cut_level'] || 4
|
|
10
|
-
@index_store = opts['index']
|
|
11
|
-
autosync! unless opts['noautosync']
|
|
12
|
-
raise "Missing chunk storage" unless @chunk_storage
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def find(uuid, version=nil, opts = {})
|
|
16
|
-
uuid_version = uuid + (version ? ".#{version}" : "")
|
|
17
|
-
master_chunk = @chunk_storage.find('MASTER')
|
|
18
|
-
return nil unless master_chunk # no master chunk yet
|
|
19
|
-
chunk_uuid = master_chunk.find_nearest(uuid_version, nil)
|
|
20
|
-
return nil unless chunk_uuid # no chunks in master chunk yet
|
|
21
|
-
chunk = @chunk_storage.find(chunk_uuid)
|
|
22
|
-
return nil unless chunk
|
|
23
|
-
|
|
24
|
-
raw_doc = chunk.find(uuid_version)
|
|
25
|
-
|
|
26
|
-
if raw_doc
|
|
27
|
-
return raw_doc if opts[:no_instantiation]
|
|
28
|
-
doc = Document.from_raw(self, raw_doc.freeze)
|
|
29
|
-
doc.extend(VersionedDocument) if version
|
|
30
|
-
return doc
|
|
31
|
-
end
|
|
32
|
-
nil
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def search(*args)
|
|
36
|
-
return [] unless @index_store
|
|
37
|
-
@index_store.find(*args)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def exists?(uuid, version=nil)
|
|
41
|
-
!!find(uuid, version, :no_instantiation => true)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def head_version(uuid)
|
|
45
|
-
raw_doc = find(uuid, nil, :no_instantiation => true)
|
|
46
|
-
return raw_doc['version'] if raw_doc
|
|
47
|
-
nil
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def save!(doc)
|
|
51
|
-
master_chunk = find_or_create_master_chunk
|
|
52
|
-
next_timestamp
|
|
53
|
-
|
|
54
|
-
insert_with_cut(doc.uuid, doc, master_chunk) unless doc.is_a?(VersionedDocument)
|
|
55
|
-
insert_with_cut("#{doc.uuid}.#{doc.version}", doc, master_chunk)
|
|
56
|
-
|
|
57
|
-
update_master_chunk!(doc, master_chunk)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def save_as_head!(doc)
|
|
61
|
-
master_chunk = find_or_create_master_chunk
|
|
62
|
-
insert_with_cut(doc.uuid, doc, master_chunk)
|
|
63
|
-
update_master_chunk!(doc, master_chunk)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def full_dump
|
|
68
|
-
puts "Full storage dump:"
|
|
69
|
-
m = @chunk_storage.find('MASTER')
|
|
70
|
-
puts "No master!" unless m
|
|
71
|
-
m.each do |node|
|
|
72
|
-
puts "[chunk: #{node.key}]"
|
|
73
|
-
chunk = @chunk_storage.find(node.value)
|
|
74
|
-
if chunk
|
|
75
|
-
chunk.each do |node|
|
|
76
|
-
puts " [doc: #{node.key}] => {uuid: #{node.value['__uuid__']}, version: #{node.value['version']}, previous_version: #{node.value['previous_version']}"
|
|
77
|
-
end
|
|
78
|
-
else
|
|
79
|
-
puts " nil! (but in MASTER somehow?...)"
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def each(options = {})
|
|
85
|
-
return nil unless m = @chunk_storage.find('MASTER') # no master chunk yet
|
|
86
|
-
after = options[:after_timestamp]
|
|
87
|
-
include_versions = options[:include_versions]
|
|
88
|
-
m.each do |node|
|
|
89
|
-
chunk = @chunk_storage.find(node.value)
|
|
90
|
-
next unless chunk
|
|
91
|
-
next if after && chunk.timestamp <= after
|
|
92
|
-
|
|
93
|
-
chunk.each do |node|
|
|
94
|
-
next if after && (node.timestamp <= after)
|
|
95
|
-
if uuid_match = node.key.match(/^#{UUID_RE}$/) || (include_versions && uuid_match = node.key.match(/#{UUID_RE}./) )
|
|
96
|
-
yield Document.from_raw(self, node.value)
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def timestamp
|
|
103
|
-
@timestamp ||= (lts = find_or_create_master_chunk.timestamp) ? LTS.from_raw(lts) : LTS.zero(uuid)
|
|
104
|
-
end
|
|
105
|
-
def next_timestamp
|
|
106
|
-
@timestamp = timestamp.next
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def uuid
|
|
110
|
-
return @uuid if @uuid
|
|
111
|
-
master_chunk = @chunk_storage.find('MASTER')
|
|
112
|
-
unless master_chunk
|
|
113
|
-
@uuid = Util.random_uuid
|
|
114
|
-
else
|
|
115
|
-
@uuid = master_chunk.store_uuid
|
|
116
|
-
end
|
|
117
|
-
@uuid
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def document
|
|
121
|
-
find(uuid) || StoreInfo.create!(self,:kind => 'skiplist', :uuid => uuid)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def empty?
|
|
125
|
-
!@chunk_storage.find('MASTER')
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def inspect
|
|
129
|
-
"#<Skiplist store #{uuid}#{empty? ? " (empty)" : ""}>"
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def autosync!
|
|
133
|
-
@autosync_mutex ||= Mutex.new
|
|
134
|
-
@autosync = nil if @autosync && !@autosync.status
|
|
135
|
-
at_exit { stop_autosync! }
|
|
136
|
-
@autosync ||= Thread.new do
|
|
137
|
-
until @stop_autosync
|
|
138
|
-
@autosync_mutex.synchronize { chunk_storage.sync_chained_storages! }
|
|
139
|
-
sleep(1)
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def stop_autosync!
|
|
145
|
-
if @autosync_mutex
|
|
146
|
-
@autosync_mutex.synchronize { @stop_autosync = true; chunk_storage.sync_chained_storages! }
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
private
|
|
152
|
-
|
|
153
|
-
def insert_with_cut(uuid, doc, master_chunk)
|
|
154
|
-
chunk_uuid = master_chunk.find_nearest(uuid)
|
|
155
|
-
unless chunk_uuid && chunk = @chunk_storage.find(chunk_uuid)
|
|
156
|
-
chunk = Chunk.new(@cut_level)
|
|
157
|
-
end
|
|
158
|
-
a, b = chunk.insert(uuid, doc.to_raw, nil, timestamp.counter)
|
|
159
|
-
[a,b].compact.each do |chunk|
|
|
160
|
-
chunk.store_uuid = self.uuid
|
|
161
|
-
chunk.timestamp = timestamp.counter
|
|
162
|
-
end
|
|
163
|
-
# if split
|
|
164
|
-
if b
|
|
165
|
-
# rename chunk if the first chunk inconsistency detected
|
|
166
|
-
if a.uuid != a.first_uuid
|
|
167
|
-
old_uuid = a.uuid
|
|
168
|
-
a.uuid = a.first_uuid
|
|
169
|
-
@chunk_storage.save!(a)
|
|
170
|
-
master_chunk.insert(a.uuid, a.uuid)
|
|
171
|
-
# remove old chunk
|
|
172
|
-
@chunk_storage.delete!(old_uuid)
|
|
173
|
-
master_chunk.delete(old_uuid)
|
|
174
|
-
else
|
|
175
|
-
@chunk_storage.save!(a)
|
|
176
|
-
master_chunk.insert(a.uuid, a.uuid)
|
|
177
|
-
end
|
|
178
|
-
@chunk_storage.save!(b)
|
|
179
|
-
master_chunk.insert(b.uuid, b.uuid)
|
|
180
|
-
else
|
|
181
|
-
@chunk_storage.save!(a)
|
|
182
|
-
master_chunk.insert(a.uuid, a.uuid)
|
|
183
|
-
end
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
def find_or_create_master_chunk
|
|
187
|
-
if master_chunk = @chunk_storage.find('MASTER')
|
|
188
|
-
return master_chunk
|
|
189
|
-
end
|
|
190
|
-
master_chunk = Chunk.new(999)
|
|
191
|
-
master_chunk.uuid = 'MASTER'
|
|
192
|
-
master_chunk.store_uuid = uuid
|
|
193
|
-
@chunk_storage.save!(master_chunk)
|
|
194
|
-
master_chunk
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
def update_master_chunk!(doc, master_chunk)
|
|
199
|
-
@chunk_storage.save!(master_chunk)
|
|
200
|
-
|
|
201
|
-
# Update index
|
|
202
|
-
if @index_store
|
|
203
|
-
if doc.previous_version
|
|
204
|
-
raw_pdoc = find(doc.uuid, doc.previous_version, :no_instantiation => true)
|
|
205
|
-
pdoc = Document.from_raw(self, raw_pdoc.freeze, :skip_callbacks => true)
|
|
206
|
-
pdoc.extend(VersionedDocument)
|
|
207
|
-
@index_store.delete(pdoc)
|
|
208
|
-
end
|
|
209
|
-
@index_store.insert(doc)
|
|
210
|
-
@index_store.save!
|
|
211
|
-
end
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
end
|
|
217
|
-
end
|