oinky 0.1.0
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/LICENSE +22 -0
- data/README.md +141 -0
- data/ext/extconf.rb +79 -0
- data/ext/include/oinky.h +424 -0
- data/ext/include/oinky.hpp +63 -0
- data/ext/include/oinky/nky_base.hpp +1116 -0
- data/ext/include/oinky/nky_core.hpp +1603 -0
- data/ext/include/oinky/nky_cursor.hpp +665 -0
- data/ext/include/oinky/nky_dialect.hpp +107 -0
- data/ext/include/oinky/nky_error.hpp +164 -0
- data/ext/include/oinky/nky_fixed_table.hpp +710 -0
- data/ext/include/oinky/nky_handle.hpp +334 -0
- data/ext/include/oinky/nky_index.hpp +1038 -0
- data/ext/include/oinky/nky_log.hpp +15 -0
- data/ext/include/oinky/nky_merge_itr.hpp +403 -0
- data/ext/include/oinky/nky_model.hpp +110 -0
- data/ext/include/oinky/nky_pool.hpp +760 -0
- data/ext/include/oinky/nky_public.hpp +808 -0
- data/ext/include/oinky/nky_serializer.hpp +1625 -0
- data/ext/include/oinky/nky_strtable.hpp +504 -0
- data/ext/include/oinky/nky_table.hpp +1996 -0
- data/ext/nky_lib.cpp +390 -0
- data/ext/nky_lib_core.hpp +212 -0
- data/ext/nky_lib_index.cpp +158 -0
- data/ext/nky_lib_table.cpp +224 -0
- data/lib/oinky.rb +1284 -0
- data/lib/oinky/compiler.rb +106 -0
- data/lib/oinky/cpp_emitter.rb +311 -0
- data/lib/oinky/dsl.rb +167 -0
- data/lib/oinky/error.rb +19 -0
- data/lib/oinky/modelbase.rb +12 -0
- data/lib/oinky/nbuffer.rb +152 -0
- data/lib/oinky/normalize.rb +132 -0
- data/lib/oinky/oc_builder.rb +44 -0
- data/lib/oinky/query.rb +193 -0
- data/lib/oinky/rb_emitter.rb +147 -0
- data/lib/oinky/shard.rb +40 -0
- data/lib/oinky/testsup.rb +104 -0
- data/lib/oinky/version.rb +9 -0
- data/oinky.gemspec +36 -0
- metadata +120 -0
@@ -0,0 +1,147 @@
|
|
1
|
+
# This source is distributed under the terms of the MIT License. Refer
|
2
|
+
# to the 'LICENSE' file for details.
|
3
|
+
#
|
4
|
+
# Copyright (c) Jacob Lacouture, 2012
|
5
|
+
|
6
|
+
require 'oinky/normalize.rb'
|
7
|
+
|
8
|
+
module Oinky
|
9
|
+
module Model
|
10
|
+
class Ruby
|
11
|
+
def initialize(schema)
|
12
|
+
@schema = Oinky::Model.normalize_schema(schema)
|
13
|
+
end
|
14
|
+
|
15
|
+
def rbify_method(str)
|
16
|
+
str = str.gsub(/[^a-z0-9A-Z_]/,'_')
|
17
|
+
return str unless /$[^a-z]/ =~ str
|
18
|
+
return "_" + str
|
19
|
+
end
|
20
|
+
|
21
|
+
def rbify_chars(str)
|
22
|
+
str = str.gsub(/[^a-z0-9A-Z_]/,'_')
|
23
|
+
end
|
24
|
+
|
25
|
+
def schema_classname
|
26
|
+
cn = @schema[:classname]
|
27
|
+
return "S#{@schema[:name].to_s}Adapter" unless cn
|
28
|
+
return cn[:ruby] if cn.is_a? Hash
|
29
|
+
return cn.call(:ruby) if cn.is_a? Proc
|
30
|
+
return cn.to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
def tbl_classname(str) ; "Table_#{rbify_chars(str)}" ; end
|
34
|
+
#def handle_from_tablename(str) ; "#{cppify(str)}_handle" ; end
|
35
|
+
#def accessor_from_tablename(str) ; cppify(str) ; end
|
36
|
+
|
37
|
+
def default_value_expression(col)
|
38
|
+
value = col
|
39
|
+
value = col[:default] if col.is_a? Hash
|
40
|
+
return "DateTime.parse(#{value.to_s.inspect})" if value.is_a? DateTime
|
41
|
+
return value.inspect
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_ruby(h)
|
45
|
+
str = ['{']
|
46
|
+
h.each {|k,v|
|
47
|
+
str << k.inspect + "=>"
|
48
|
+
if v.is_a? Hash
|
49
|
+
str << to_ruby(v)
|
50
|
+
elsif v.is_a? DateTime
|
51
|
+
str << default_value_expression(v)
|
52
|
+
else
|
53
|
+
str << v.inspect
|
54
|
+
end
|
55
|
+
str << ','
|
56
|
+
}
|
57
|
+
str << '}'
|
58
|
+
return str * ''
|
59
|
+
end
|
60
|
+
|
61
|
+
# Generate a string, containing the Ruby code for the model classes.
|
62
|
+
# this can be eval'd directly in ruby, or saved and required
|
63
|
+
# more normally. Supporting both lends itself to static analysis
|
64
|
+
# and transformation moreso than if we solved this with ruby
|
65
|
+
# metaprogramming.
|
66
|
+
#
|
67
|
+
def emit
|
68
|
+
p = Oinky::Detail::Builder.new
|
69
|
+
schemacls = self.schema_classname
|
70
|
+
p.next("class #{schemacls}", "end ##{schemacls}") {
|
71
|
+
tables = @schema[:tables]
|
72
|
+
p.next("def self.schema", "end") {
|
73
|
+
p << to_ruby(@schema.merge(:classname=>schemacls))
|
74
|
+
}
|
75
|
+
p.next("def schema", "end") {
|
76
|
+
p << 'self.class.schema'
|
77
|
+
}
|
78
|
+
p << ''
|
79
|
+
p.next("def inspect", "end") {
|
80
|
+
# Hide all the internal state, which gets really obnoxious
|
81
|
+
p << '"#<#{self.class}:0x#{self.__id__.to_s(16)}>"'
|
82
|
+
# This is just a bit long to include.
|
83
|
+
#schema=#{self.schema.to_s}>"'
|
84
|
+
}
|
85
|
+
p << ''
|
86
|
+
tables.each{|nm,t|
|
87
|
+
clsname = tbl_classname(nm)
|
88
|
+
p.next("class #{clsname}","end ##{clsname}") {
|
89
|
+
p << "include Oinky::Model::Table"
|
90
|
+
p << ''
|
91
|
+
p.next("def initialize(tbl)","end") {
|
92
|
+
p << "@tbl = tbl"
|
93
|
+
p << "self.class.migrate(tbl)"
|
94
|
+
p << ''
|
95
|
+
t[:indices].each {|k,v|
|
96
|
+
accessor = rbify_method(v[:accessor])
|
97
|
+
p << "@#{accessor} = @tbl.indices[#{k.inspect}]"
|
98
|
+
}
|
99
|
+
}
|
100
|
+
p << ''
|
101
|
+
p.next("def self.migrate(tbl)","end") {
|
102
|
+
p.next("tbl.db.atomic {", "}") {
|
103
|
+
p << "cols = tbl.columns"
|
104
|
+
t[:columns].each{|k,v|
|
105
|
+
p.next("if c = cols[#{k.inspect}]","end") {
|
106
|
+
p << "raise OinkyException.new('Mismatched column type for column [#{k.to_s}]') unless c[:type] == :#{v[:type]}"
|
107
|
+
p.write("else",-1)
|
108
|
+
p << "tbl.add_column(#{k.inspect},#{v[:type].inspect},#{default_value_expression(v)})"
|
109
|
+
}
|
110
|
+
}
|
111
|
+
p << "ixs = tbl.indices"
|
112
|
+
t[:indices].each{|k,v|
|
113
|
+
p.next("unless ixs[#{k.inspect}]","end") {
|
114
|
+
p << "tbl.add_index(:name=>#{k.inspect},:unique=>#{v[:unique]},:columns=>#{v[:columns]})"
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
118
|
+
}
|
119
|
+
p << ''
|
120
|
+
t[:indices].each {|k,v|
|
121
|
+
accessor = rbify_method(v[:accessor])
|
122
|
+
p << "attr_accessor :#{accessor}"
|
123
|
+
}
|
124
|
+
p << "def table ; @tbl ; end"
|
125
|
+
p << "alias :t :table"
|
126
|
+
} #tablecls
|
127
|
+
p << ''
|
128
|
+
} #tables.each
|
129
|
+
|
130
|
+
p.next("def initialize(db, prefix = '')","end") {
|
131
|
+
tables.each{|nm,t|
|
132
|
+
accessor = rbify_method(t[:accessor])
|
133
|
+
p << "tn = prefix + #{nm.inspect}"
|
134
|
+
p << "db[tn] or db.create_table(tn)"
|
135
|
+
p << "@#{accessor} = #{tbl_classname(nm)}.new(db[tn])"
|
136
|
+
}
|
137
|
+
}
|
138
|
+
tables.each{|nm,t|
|
139
|
+
accessor = rbify_method(t[:accessor])
|
140
|
+
p << "attr_accessor :#{accessor}"
|
141
|
+
}
|
142
|
+
} #schemacls
|
143
|
+
return p.format
|
144
|
+
end #emit
|
145
|
+
end #class Ruby
|
146
|
+
end #Model
|
147
|
+
end #Oinky
|
data/lib/oinky/shard.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
s = build_schema {
|
2
|
+
create_table(:foo) {
|
3
|
+
string :key
|
4
|
+
string :type
|
5
|
+
string :payload
|
6
|
+
|
7
|
+
datetime :timestamp
|
8
|
+
|
9
|
+
# Create an index on this table (called 'id') which is unique
|
10
|
+
# over the given columnset. The resolver requires that this field
|
11
|
+
# be identified.
|
12
|
+
identity [:key]
|
13
|
+
|
14
|
+
# When facing two identical
|
15
|
+
resolve_on :timestamp
|
16
|
+
|
17
|
+
#shard_on :key
|
18
|
+
#shard_on lambda {|r| r[:key]}
|
19
|
+
}
|
20
|
+
|
21
|
+
create_table(:footype) {
|
22
|
+
# Make sure that on any shard, all types are available
|
23
|
+
shard_join([:foo,:type], [:footype,:type])
|
24
|
+
}
|
25
|
+
|
26
|
+
# This is how we generate the names of the storage objects.
|
27
|
+
# fooset_0, fooset_1, etc.
|
28
|
+
shardset_name :fooset
|
29
|
+
|
30
|
+
}
|
31
|
+
|
32
|
+
# A table spans many shards. To search for an exact row, we can generate
|
33
|
+
# a shard key from the given row. However, to enumerate a range of rows,
|
34
|
+
# we need to generate a range of shard keys. (The range may only be one
|
35
|
+
# shard...but in general we'll need to do enumerations across shards).
|
36
|
+
# The atlantic DB is 150BM with just half of 2012.
|
37
|
+
|
38
|
+
#In the implementation, we just need a set of shard keys.
|
39
|
+
|
40
|
+
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# Test-related support code for oinky
|
2
|
+
|
3
|
+
module Oinky
|
4
|
+
module Test
|
5
|
+
|
6
|
+
class DBCompare
|
7
|
+
def check_eq(l,r)
|
8
|
+
raise RuntimeError.new("Inequality: [#{l.inspect}] != [#{r.inspect}] Context:[#{@context.to_s}]") unless l == r
|
9
|
+
end
|
10
|
+
|
11
|
+
def kv_compare(ls, rs, fn)
|
12
|
+
check_eq(ls.size,rs.size)
|
13
|
+
ls.each{|k,v|
|
14
|
+
@context.push k
|
15
|
+
fn.call(v, rs[k])
|
16
|
+
@context.pop
|
17
|
+
}
|
18
|
+
end
|
19
|
+
def seq_compare(ls, rs, fn, neq_prev = false)
|
20
|
+
l = ls.each
|
21
|
+
r = rs.each
|
22
|
+
count = 0
|
23
|
+
last = self
|
24
|
+
begin
|
25
|
+
while true
|
26
|
+
left = true
|
27
|
+
lv = l.next
|
28
|
+
left = false
|
29
|
+
rv = r.next
|
30
|
+
@context.push count
|
31
|
+
fn.call(lv,rv)
|
32
|
+
|
33
|
+
# If we are verifying that each list is also unique, then
|
34
|
+
# check against the previous value
|
35
|
+
if neq_prev and (last != self)
|
36
|
+
# We EXPECT this comparison to raise NEQ
|
37
|
+
peq = false
|
38
|
+
begin
|
39
|
+
fn.call(last, lv)
|
40
|
+
peq = true
|
41
|
+
rescue
|
42
|
+
end
|
43
|
+
if peq
|
44
|
+
check_eq("Sequence is not unique",nil)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
last = lv
|
48
|
+
|
49
|
+
@context.pop
|
50
|
+
count += 1
|
51
|
+
end
|
52
|
+
rescue StopIteration
|
53
|
+
# Left should have raised.
|
54
|
+
check_eq(left, true)
|
55
|
+
end
|
56
|
+
# verify that the right enumerator is also done
|
57
|
+
begin
|
58
|
+
r.next
|
59
|
+
# These are obviously not equal if the above didn't raise.
|
60
|
+
check_eq("nonempty enumerator",nil)
|
61
|
+
rescue StopIteration
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def table_compare(l, r)
|
66
|
+
kv_compare(l.columns, r.columns, self.method(:check_eq))
|
67
|
+
check_eq(l.row_count, r.row_count)
|
68
|
+
# Table ordering is not guaranteed to match. We have to sort.
|
69
|
+
# However, we may have to sort over variant types, so convert
|
70
|
+
# everything to a string before ordering.
|
71
|
+
seq_compare(
|
72
|
+
l.cursor_each.map {|c| c.select_all_values.map{|v| v.to_s} }.sort,
|
73
|
+
r.cursor_each.map {|c| c.select_all_values.map{|v| v.to_s} }.sort,
|
74
|
+
self.method(:check_eq))
|
75
|
+
kv_compare(l.indices, r.indices, self.method(:index_compare))
|
76
|
+
end
|
77
|
+
|
78
|
+
def index_compare(l, r)
|
79
|
+
kv_compare(l.definition, r.definition, self.method(:check_eq))
|
80
|
+
check_eq(l.row_count, r.row_count)
|
81
|
+
# We will only compare the indexed columns. Building the selectors
|
82
|
+
# outside of the loop saves work.
|
83
|
+
ls = l.selector
|
84
|
+
rs = r.selector
|
85
|
+
seq_compare(l.each(ls), r.each(rs), self.method(:check_eq), l.definition[:unique])
|
86
|
+
end
|
87
|
+
|
88
|
+
def compare(l, r)
|
89
|
+
begin
|
90
|
+
kv_compare(l.tables, r.tables, self.method(:table_compare))
|
91
|
+
rescue StandardError => e
|
92
|
+
puts "Exception comparing database instances: [#{@context}]"
|
93
|
+
raise e
|
94
|
+
end
|
95
|
+
self
|
96
|
+
end
|
97
|
+
def initialize
|
98
|
+
@context = []
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
end #module Test
|
104
|
+
end #module Oinky
|
data/oinky.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
class T
|
4
|
+
def self.expand_dirname(__file__)
|
5
|
+
File.expand_path(__file__).sub(/\/[^\/]+$/,'')
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
gem_root = T.expand_dirname(__FILE__)
|
10
|
+
|
11
|
+
require(File.join(gem_root,'lib/oinky/version.rb'))
|
12
|
+
|
13
|
+
Gem::Specification.new do |s|
|
14
|
+
s.name = "oinky"
|
15
|
+
s.version = Oinky::VERSION
|
16
|
+
# There's no version of FFI that's consistent across MRI, JRuby, and RBX.
|
17
|
+
# Hopefully that will get fixed, but for now it has to be MRI.
|
18
|
+
# The relevant issues are FFI::Struct.by_value and MemoryPointer.get_int64
|
19
|
+
s.platform = Gem::Platform::RUBY #['mri_19'] <== this isn't recognized??
|
20
|
+
s.authors = ["Jacob Lacouture"]
|
21
|
+
s.email = ["jacobx984@gmail.com"]
|
22
|
+
s.homepage = "http://github.com/oinky/oinky"
|
23
|
+
s.summary = "Relational data serialization library"
|
24
|
+
s.description = "Oinky is an in-memory relational database which can serialize and mount extremely quickly, making it an ideal solution for relational microsharding atop distributed key-value storage."
|
25
|
+
|
26
|
+
s.rubyforge_project = "oinky"
|
27
|
+
|
28
|
+
s.add_development_dependency "rake"
|
29
|
+
s.add_dependency('ffi','>= 1.0.11')
|
30
|
+
|
31
|
+
s.files = Dir.glob("**/*")
|
32
|
+
s.executables = []
|
33
|
+
s.extensions << "ext/extconf.rb"
|
34
|
+
|
35
|
+
s.require_path = 'lib'
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: oinky
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jacob Lacouture
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: ffi
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.0.11
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.0.11
|
46
|
+
description: Oinky is an in-memory relational database which can serialize and mount
|
47
|
+
extremely quickly, making it an ideal solution for relational microsharding atop
|
48
|
+
distributed key-value storage.
|
49
|
+
email:
|
50
|
+
- jacobx984@gmail.com
|
51
|
+
executables: []
|
52
|
+
extensions:
|
53
|
+
- ext/extconf.rb
|
54
|
+
extra_rdoc_files: []
|
55
|
+
files:
|
56
|
+
- ext/extconf.rb
|
57
|
+
- ext/include/oinky/nky_base.hpp
|
58
|
+
- ext/include/oinky/nky_core.hpp
|
59
|
+
- ext/include/oinky/nky_cursor.hpp
|
60
|
+
- ext/include/oinky/nky_dialect.hpp
|
61
|
+
- ext/include/oinky/nky_error.hpp
|
62
|
+
- ext/include/oinky/nky_fixed_table.hpp
|
63
|
+
- ext/include/oinky/nky_handle.hpp
|
64
|
+
- ext/include/oinky/nky_index.hpp
|
65
|
+
- ext/include/oinky/nky_log.hpp
|
66
|
+
- ext/include/oinky/nky_merge_itr.hpp
|
67
|
+
- ext/include/oinky/nky_model.hpp
|
68
|
+
- ext/include/oinky/nky_pool.hpp
|
69
|
+
- ext/include/oinky/nky_public.hpp
|
70
|
+
- ext/include/oinky/nky_serializer.hpp
|
71
|
+
- ext/include/oinky/nky_strtable.hpp
|
72
|
+
- ext/include/oinky/nky_table.hpp
|
73
|
+
- ext/include/oinky.h
|
74
|
+
- ext/include/oinky.hpp
|
75
|
+
- ext/nky_lib.cpp
|
76
|
+
- ext/nky_lib_core.hpp
|
77
|
+
- ext/nky_lib_index.cpp
|
78
|
+
- ext/nky_lib_table.cpp
|
79
|
+
- lib/oinky/compiler.rb
|
80
|
+
- lib/oinky/cpp_emitter.rb
|
81
|
+
- lib/oinky/dsl.rb
|
82
|
+
- lib/oinky/error.rb
|
83
|
+
- lib/oinky/modelbase.rb
|
84
|
+
- lib/oinky/nbuffer.rb
|
85
|
+
- lib/oinky/normalize.rb
|
86
|
+
- lib/oinky/oc_builder.rb
|
87
|
+
- lib/oinky/query.rb
|
88
|
+
- lib/oinky/rb_emitter.rb
|
89
|
+
- lib/oinky/shard.rb
|
90
|
+
- lib/oinky/testsup.rb
|
91
|
+
- lib/oinky/version.rb
|
92
|
+
- lib/oinky.rb
|
93
|
+
- LICENSE
|
94
|
+
- oinky.gemspec
|
95
|
+
- README.md
|
96
|
+
homepage: http://github.com/oinky/oinky
|
97
|
+
licenses: []
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
none: false
|
104
|
+
requirements:
|
105
|
+
- - ! '>='
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ! '>='
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
requirements: []
|
115
|
+
rubyforge_project: oinky
|
116
|
+
rubygems_version: 1.8.24
|
117
|
+
signing_key:
|
118
|
+
specification_version: 3
|
119
|
+
summary: Relational data serialization library
|
120
|
+
test_files: []
|