perobs 1.0.0 → 1.0.1
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.
- checksums.yaml +4 -4
- data/README.md +4 -4
- data/lib/perobs/BTreeDB.rb +7 -1
- data/lib/perobs/DynamoDB.rb +250 -0
- data/lib/perobs/version.rb +1 -1
- data/perobs.gemspec +1 -0
- data/spec/BTreeDB_spec.rb +4 -9
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8576de664206470a3b075101951c4a65144d643
|
4
|
+
data.tar.gz: 26a46db076eea54ca4dbb5ed6312a3ec855971d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fac5c8714eba1be5beb6647330044177ea4eff24ada10243e0a3a4dc595eb7316662a4fdf0e63e8faf07f9b57eff15d232e66a63a4d3a84019d1b8b2d5a08b70
|
7
|
+
data.tar.gz: 93a75aeb2430c57535f6a4fb792db48717c1278d22939be9168e9341c4dddd94565de6adca739ebde76b6d72845f1800394b0aa8ed24da7488f6f10c1772a539
|
data/README.md
CHANGED
@@ -60,7 +60,7 @@ class Person < PEROBS::Object
|
|
60
60
|
def initialize(store, name)
|
61
61
|
super
|
62
62
|
attr_init(:name, name)
|
63
|
-
attr_init(:kids, PEROBS::Array.new)
|
63
|
+
attr_init(:kids, PEROBS::Array.new(store))
|
64
64
|
end
|
65
65
|
|
66
66
|
def to_s
|
@@ -71,9 +71,9 @@ class Person < PEROBS::Object
|
|
71
71
|
end
|
72
72
|
|
73
73
|
store = PEROBS::Store.new('family')
|
74
|
-
store['grandpa'] = joe = Person.new('Joe')
|
75
|
-
store['grandma'] = jane = Person.new('Jane')
|
76
|
-
jim = Person.new('Jim')
|
74
|
+
store['grandpa'] = joe = Person.new(store, 'Joe')
|
75
|
+
store['grandma'] = jane = Person.new(store, 'Jane')
|
76
|
+
jim = Person.new(store, 'Jim')
|
77
77
|
jim.father = joe
|
78
78
|
joe.kids << jim
|
79
79
|
jim.mother = jane
|
data/lib/perobs/BTreeDB.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
#
|
3
|
-
# =
|
3
|
+
# = BTreeDB.rb -- Persistent Ruby Object Store
|
4
4
|
#
|
5
5
|
# Copyright (c) 2015 by Chris Schlaeger <chris@taskjuggler.org>
|
6
6
|
#
|
@@ -25,6 +25,8 @@
|
|
25
25
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
26
26
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27
27
|
|
28
|
+
require 'fileutils'
|
29
|
+
|
28
30
|
require 'perobs/DataBase'
|
29
31
|
require 'perobs/BTreeBlob'
|
30
32
|
|
@@ -88,6 +90,10 @@ module PEROBS
|
|
88
90
|
@dir_mask = 2 ** @dir_bits - 1
|
89
91
|
end
|
90
92
|
|
93
|
+
def BTreeDB::delete_db(db_name)
|
94
|
+
FileUtils.rm_rf(db_name)
|
95
|
+
end
|
96
|
+
|
91
97
|
# Return true if the object with given ID exists
|
92
98
|
# @param id [Fixnum or Bignum]
|
93
99
|
def include?(id)
|
@@ -0,0 +1,250 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# = DynamoDB.rb -- Persistent Ruby Object Store
|
4
|
+
#
|
5
|
+
# Copyright (c) 2015 by Chris Schlaeger <chris@taskjuggler.org>
|
6
|
+
#
|
7
|
+
# MIT License
|
8
|
+
#
|
9
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
10
|
+
# a copy of this software and associated documentation files (the
|
11
|
+
# "Software"), to deal in the Software without restriction, including
|
12
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
13
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
14
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
15
|
+
# the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be
|
18
|
+
# included in all copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
21
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
23
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
24
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
25
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
26
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27
|
+
|
28
|
+
require 'aws-sdk-core'
|
29
|
+
|
30
|
+
require 'perobs/DataBase'
|
31
|
+
require 'perobs/BTreeBlob'
|
32
|
+
|
33
|
+
module PEROBS
|
34
|
+
|
35
|
+
# This class implements an Amazon DynamoDB storage engine for PEROBS.
|
36
|
+
class DynamoDB < DataBase
|
37
|
+
|
38
|
+
# Create a new DynamoDB object.
|
39
|
+
# @param db_name [String] name of the DB directory
|
40
|
+
# @param options [Hash] options to customize the behavior. Currently only
|
41
|
+
# the following options are supported:
|
42
|
+
# :serializer : Can be :json and :yaml
|
43
|
+
# :aws_id : AWS credentials ID
|
44
|
+
# :aws_key : AWS credentials key
|
45
|
+
# :aws_region : AWS region to host the data
|
46
|
+
def initialize(db_name, options = {})
|
47
|
+
# :marshal serialization results in a binary format that cannot easily
|
48
|
+
# be stored in DynamoDB. We fall back to :yaml.
|
49
|
+
if options[:serializer] == :marshal
|
50
|
+
options[:serializer] = :yaml
|
51
|
+
end
|
52
|
+
|
53
|
+
super(options[:serializer] || :json)
|
54
|
+
|
55
|
+
if options.include?(:aws_id) && options.include?(:aws_key)
|
56
|
+
Aws.config[:credentials] = Aws::Credentials.new(options[:aws_id],
|
57
|
+
options[:aws_key])
|
58
|
+
end
|
59
|
+
if options.include?(:aws_region)
|
60
|
+
Aws.config[:region] = options[:aws_region]
|
61
|
+
end
|
62
|
+
|
63
|
+
@dynamodb = Aws::DynamoDB::Client.new
|
64
|
+
@table_name = db_name
|
65
|
+
ensure_table_exists(@table_name)
|
66
|
+
|
67
|
+
# Read the existing DB config.
|
68
|
+
@config = get_hash('config')
|
69
|
+
check_option('serializer')
|
70
|
+
put_hash('config', @config)
|
71
|
+
end
|
72
|
+
|
73
|
+
def DynamoDB::delete_db(table_name)
|
74
|
+
dynamodb = Aws::DynamoDB::Client.new
|
75
|
+
dynamodb.delete_table(:table_name => table_name)
|
76
|
+
dynamodb.wait_until(:table_not_exists, table_name: table_name)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Return true if the object with given ID exists
|
80
|
+
# @param id [Fixnum or Bignum]
|
81
|
+
def include?(id)
|
82
|
+
dynamo_get_item(id.to_s)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Store a simple Hash as a JSON encoded file into the DB directory.
|
86
|
+
# @param name [String] Name of the hash. Will be used as file name.
|
87
|
+
# @param hash [Hash] A Hash that maps String objects to strings or
|
88
|
+
# numbers.
|
89
|
+
def put_hash(name, hash)
|
90
|
+
dynamo_put_item(name, hash.to_json)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Load the Hash with the given name.
|
94
|
+
# @param name [String] Name of the hash.
|
95
|
+
# @return [Hash] A Hash that maps String objects to strings or numbers.
|
96
|
+
def get_hash(name)
|
97
|
+
if (item = dynamo_get_item(name))
|
98
|
+
JSON.parse(item)
|
99
|
+
else
|
100
|
+
::Hash.new
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Store the given object into the cluster files.
|
105
|
+
# @param obj [Hash] Object as defined by PEROBS::ObjectBase
|
106
|
+
def put_object(obj, id)
|
107
|
+
dynamo_put_item(id.to_s, serialize(obj))
|
108
|
+
end
|
109
|
+
|
110
|
+
# Load the given object from the filesystem.
|
111
|
+
# @param id [Fixnum or Bignum] object ID
|
112
|
+
# @return [Hash] Object as defined by PEROBS::ObjectBase or nil if ID does
|
113
|
+
# not exist
|
114
|
+
def get_object(id)
|
115
|
+
(item = dynamo_get_item(id.to_s)) ? deserialize(item) : nil
|
116
|
+
end
|
117
|
+
|
118
|
+
# This method must be called to initiate the marking process.
|
119
|
+
def clear_marks
|
120
|
+
each_item do |id|
|
121
|
+
dynamo_mark_item(id, false)
|
122
|
+
end
|
123
|
+
# Mark the 'config' item so it will not get deleted.
|
124
|
+
dynamo_mark_item('config')
|
125
|
+
end
|
126
|
+
|
127
|
+
# Permanently delete all objects that have not been marked. Those are
|
128
|
+
# orphaned and are no longer referenced by any actively used object.
|
129
|
+
def delete_unmarked_objects
|
130
|
+
each_item do |id|
|
131
|
+
dynamo_delete_item(id) unless dynamo_is_marked?(id)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Mark an object.
|
136
|
+
# @param id [Fixnum or Bignum] ID of the object to mark
|
137
|
+
def mark(id)
|
138
|
+
dynamo_mark_item(id.to_s, true)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Check if the object is marked.
|
142
|
+
# @param id [Fixnum or Bignum] ID of the object to check
|
143
|
+
def is_marked?(id)
|
144
|
+
dynamo_is_marked?(id.to_s)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Basic consistency check.
|
148
|
+
# @param repair [TrueClass/FalseClass] True if found errors should be
|
149
|
+
# repaired.
|
150
|
+
def check_db(repair = false)
|
151
|
+
# TODO: See if we can add checks here
|
152
|
+
end
|
153
|
+
|
154
|
+
# Check if the stored object is syntactically correct.
|
155
|
+
# @param id [Fixnum/Bignum] Object ID
|
156
|
+
# @param repair [TrueClass/FalseClass] True if an repair attempt should be
|
157
|
+
# made.
|
158
|
+
# @return [TrueClass/FalseClass] True if the object is OK, otherwise
|
159
|
+
# false.
|
160
|
+
def check(id, repair)
|
161
|
+
begin
|
162
|
+
get_object(id)
|
163
|
+
rescue => e
|
164
|
+
$stderr.puts "Cannot read object with ID #{id}: #{e.message}"
|
165
|
+
return false
|
166
|
+
end
|
167
|
+
|
168
|
+
true
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
def ensure_table_exists(table_name)
|
174
|
+
begin
|
175
|
+
@dynamodb.describe_table(:table_name => table_name)
|
176
|
+
rescue Aws::DynamoDB::Errors::ResourceNotFoundException
|
177
|
+
@dynamodb.create_table(
|
178
|
+
:table_name => table_name,
|
179
|
+
:attribute_definitions => [
|
180
|
+
{
|
181
|
+
:attribute_name => :Id,
|
182
|
+
:attribute_type => :S
|
183
|
+
}
|
184
|
+
],
|
185
|
+
:key_schema => [
|
186
|
+
{
|
187
|
+
:attribute_name => :Id,
|
188
|
+
:key_type => :HASH
|
189
|
+
}
|
190
|
+
],
|
191
|
+
:provisioned_throughput => {
|
192
|
+
:read_capacity_units => 1,
|
193
|
+
:write_capacity_units => 1,
|
194
|
+
}
|
195
|
+
)
|
196
|
+
|
197
|
+
@dynamodb.wait_until(:table_exists, table_name: table_name)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def dynamo_get_item(id)
|
202
|
+
resp = @dynamodb.get_item(:table_name => @table_name,
|
203
|
+
:key => { :Id => id })
|
204
|
+
resp[:item] ? resp[:item]['Value'] : nil
|
205
|
+
end
|
206
|
+
|
207
|
+
def dynamo_put_item(id, value)
|
208
|
+
@dynamodb.put_item(:table_name => @table_name,
|
209
|
+
:item => { :Id => id, :Value => value })
|
210
|
+
end
|
211
|
+
|
212
|
+
def dynamo_delete_item(id)
|
213
|
+
@dynamodb.delete_item(:table_name => @table_name,
|
214
|
+
:key => { :Id => id })
|
215
|
+
end
|
216
|
+
|
217
|
+
def dynamo_mark_item(id, set_mark = true)
|
218
|
+
@dynamodb.update_item(:table_name => @table_name,
|
219
|
+
:key => { :Id => id },
|
220
|
+
:attribute_updates => {
|
221
|
+
:Mark => { :value => set_mark,
|
222
|
+
:action => "PUT" }})
|
223
|
+
end
|
224
|
+
|
225
|
+
def dynamo_is_marked?(id)
|
226
|
+
resp = @dynamodb.get_item(:table_name => @table_name,
|
227
|
+
:key => { :Id => id })
|
228
|
+
resp[:item] && resp[:item]['Mark']
|
229
|
+
end
|
230
|
+
|
231
|
+
def each_item
|
232
|
+
start_key = nil
|
233
|
+
loop do
|
234
|
+
resp = @dynamodb.scan(:table_name => @table_name,
|
235
|
+
:exclusive_start_key => start_key)
|
236
|
+
break if resp.count <= 0
|
237
|
+
|
238
|
+
resp.items.each do |item|
|
239
|
+
yield(item['Id'])
|
240
|
+
end
|
241
|
+
|
242
|
+
break unless resp.last_evaluated_key
|
243
|
+
start_key = resp.last_evaluated_key['AttributeName']
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
248
|
+
|
249
|
+
end
|
250
|
+
|
data/lib/perobs/version.rb
CHANGED
data/perobs.gemspec
CHANGED
@@ -16,6 +16,7 @@ GEM_SPEC = Gem::Specification.new do |spec|
|
|
16
16
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ["lib"]
|
19
|
+
spec.required_ruby_version = '>=2.0'
|
19
20
|
|
20
21
|
spec.add_development_dependency 'bundler', '~> 1.6'
|
21
22
|
spec.add_development_dependency 'yard', '~>0.8.7'
|
data/spec/BTreeDB_spec.rb
CHANGED
@@ -23,24 +23,19 @@
|
|
23
23
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
24
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
25
|
|
26
|
-
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
27
|
-
|
28
|
-
require 'fileutils'
|
29
26
|
require 'time'
|
30
27
|
|
28
|
+
require 'spec_helper'
|
31
29
|
require 'perobs/BTreeDB'
|
32
30
|
|
33
31
|
describe PEROBS::BTreeDB do
|
34
32
|
|
35
|
-
class UStruct < Struct.new(:first, :second, :third)
|
36
|
-
end
|
37
|
-
|
38
33
|
before(:all) do
|
39
|
-
|
34
|
+
PEROBS::BTreeDB::delete_db('fs_test')
|
40
35
|
end
|
41
36
|
|
42
37
|
after(:each) do
|
43
|
-
|
38
|
+
PEROBS::BTreeDB::delete_db('fs_test')
|
44
39
|
end
|
45
40
|
|
46
41
|
it 'should create database' do
|
@@ -93,7 +88,7 @@ describe PEROBS::BTreeDB do
|
|
93
88
|
@db.include?(0).should be_true
|
94
89
|
@db.check(0, false).should be_true
|
95
90
|
@db.get_object(0).should == h
|
96
|
-
|
91
|
+
PEROBS::BTreeDB::delete_db('fs_test')
|
97
92
|
end
|
98
93
|
end
|
99
94
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: perobs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Schlaeger
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-09-
|
11
|
+
date: 2015-09-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -71,6 +71,7 @@ files:
|
|
71
71
|
- lib/perobs/Cache.rb
|
72
72
|
- lib/perobs/ClassMap.rb
|
73
73
|
- lib/perobs/DataBase.rb
|
74
|
+
- lib/perobs/DynamoDB.rb
|
74
75
|
- lib/perobs/Hash.rb
|
75
76
|
- lib/perobs/Object.rb
|
76
77
|
- lib/perobs/ObjectBase.rb
|
@@ -100,7 +101,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
100
101
|
requirements:
|
101
102
|
- - '>='
|
102
103
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
104
|
+
version: '2.0'
|
104
105
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
106
|
requirements:
|
106
107
|
- - '>='
|