tiny_ds 0.0.2 → 0.0.3.pre

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/spec/gae_spec.rb ADDED
@@ -0,0 +1,86 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+
4
+ describe "transaction" do
5
+ it "should push/pop tx" do
6
+ ds = AppEngine::Datastore.service
7
+ proc{ ds.getCurrentTransaction }.should raise_error(NativeException, "java.lang.IllegalStateException: java.util.NoSuchElementException")
8
+ ds.getActiveTransactions.to_a.size.should == 0
9
+
10
+ tx1 = ds.beginTransaction
11
+ # [tx1]
12
+ tx1.should be_kind_of(com.google.appengine.api.datastore.Transaction)
13
+ tx1.should == ds.getCurrentTransaction
14
+ ds.getActiveTransactions.to_a.size.should == 1
15
+
16
+ tx2 = ds.beginTransaction
17
+ # [tx2, tx1]
18
+ tx2.should be_kind_of(com.google.appengine.api.datastore.Transaction)
19
+ tx2.should == ds.getCurrentTransaction
20
+ ds.getActiveTransactions.to_a.size.should == 2
21
+ ds.getActiveTransactions.to_a[0].should == tx2
22
+ ds.getActiveTransactions.to_a[1].should == tx1
23
+
24
+ tx3 = ds.beginTransaction
25
+ # [tx3, tx2, tx1]
26
+ tx3.should be_kind_of(com.google.appengine.api.datastore.Transaction)
27
+ tx3.should == ds.getCurrentTransaction
28
+ ds.getActiveTransactions.to_a.size.should == 3
29
+ ds.getActiveTransactions.to_a[0].should == tx3
30
+ ds.getActiveTransactions.to_a[1].should == tx2
31
+ ds.getActiveTransactions.to_a[2].should == tx1
32
+
33
+ tx3.commit
34
+ # [tx2, tx1]
35
+ tx2.should == ds.getCurrentTransaction
36
+ ds.getActiveTransactions.to_a.size.should == 2
37
+ ds.getActiveTransactions.to_a[0].should == tx2
38
+ ds.getActiveTransactions.to_a[1].should == tx1
39
+
40
+ tx4 = ds.beginTransaction
41
+ # [tx4, tx2, tx1]
42
+ tx4.should be_kind_of(com.google.appengine.api.datastore.Transaction)
43
+ tx4.should == ds.getCurrentTransaction
44
+ ds.getActiveTransactions.to_a.size.should == 3
45
+ ds.getActiveTransactions.to_a[0].should == tx4
46
+ ds.getActiveTransactions.to_a[1].should == tx2
47
+ ds.getActiveTransactions.to_a[2].should == tx1
48
+
49
+ tx2.commit
50
+ # [tx4, tx1]
51
+ tx4.should == ds.getCurrentTransaction
52
+ ds.getActiveTransactions.to_a.size.should == 2
53
+ ds.getActiveTransactions.to_a[0].should == tx4
54
+ ds.getActiveTransactions.to_a[1].should == tx1
55
+
56
+ tx1.rollback
57
+ # [tx4]
58
+ tx4.should == ds.getCurrentTransaction
59
+ ds.getActiveTransactions.to_a.size.should == 1
60
+ ds.getActiveTransactions.to_a[0].should == tx4
61
+
62
+ tx4.rollback
63
+ # []
64
+ ds.getCurrentTransaction(nil).should be_nil
65
+ ds.getActiveTransactions.to_a.size.should == 0
66
+ end
67
+
68
+ it "should be default is NONE" do
69
+ dsf = com.google.appengine.api.datastore.DatastoreServiceFactory
70
+ dsc = dsf.getDefaultDatastoreConfig
71
+ dsc.should == com.google.appengine.api.datastore.DatastoreConfig::DEFAULT
72
+ dsc.getImplicitTransactionManagementPolicy.should == com.google.appengine.api.datastore.ImplicitTransactionManagementPolicy::NONE
73
+ end
74
+
75
+ it "should be work tx many settings."
76
+ =begin
77
+ Policy : AUTO or NONE
78
+ currentTx : nil or exist
79
+ method : put(ent) or put(tx, ent) or put(nil, ent)
80
+ 12 patterns.
81
+ put([e1,e2])
82
+ =end
83
+
84
+ end
85
+
86
+
data/spec/tx_spec.rb ADDED
@@ -0,0 +1,82 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ # http://www.slideshare.net/ashigeru/ajn4
4
+ # 1. EGでのACIDトランザクションの仕組み
5
+ # 2. パターン「read-modify-write」1エンティティの書き換え
6
+ # 3. パターン「トランザクションの合成」同一EG複数エンティティの書き換え
7
+ # 4. パターン「ユニーク制約」id/name指定を用いたユニーク制約の実現
8
+ # 5. パターン「べき等な処理」1回以上複数回実行されても結果は1回だけ実行された状態になる処理
9
+ # 6. パターン「exactly-once」確実に1回だけ実行される処理。2回以上実行されない処理
10
+ # 7. パターン「BASE Transaction」過渡的な状態があるトランザクション
11
+
12
+ =begin
13
+ describe "TinyDS::Transaction" do
14
+ describe "(1) EG transaction" do
15
+ it "should work" do
16
+ Tx.atomic{
17
+ }
18
+ end
19
+ end
20
+ describe "(2) read_modify_write" do
21
+ end
22
+ describe "(3) 2 entities read_modify_write" do
23
+ end
24
+ describe "(4) unique value" do
25
+ it "should work" do
26
+ User.create(props, {:name=>"hoge@example.com", :check_unique=>true})
27
+ end
28
+ # name/id が指定された場合ユニークチェックをする
29
+ # デフォルトでする / しない?
30
+ # transaction内の場合 => ...
31
+ # transaction外の場合 => ...
32
+ end
33
+ describe "(5) idempotent" do
34
+ idempotent_key = TxFlag.generate_key(@parent.key, unique_value)
35
+ args = {:amount=>5000}
36
+ Tx.idempotent(idempotent_key, args) do |args|
37
+ # Tx.atomic{
38
+ # flag = get(idempotent_key)
39
+ # raise :not_unique if flag
40
+ # yield(args){
41
+ @user.money += args[:amount]
42
+ # }
43
+ # }
44
+ end
45
+ # @parentの子としてIdempotentFlagが作成される
46
+ # @parentと(IdempotentFlagと)処理内で使うすべてのentityは同一EGである必要がある
47
+ end
48
+ describe "(6) exactly_once" do
49
+ exactly_once_key = TxFlag.generate_key(@parent.key, unique_value)
50
+ Tx.exactly_once(exactly_once_key, args) do |args|
51
+ # idempotent_key = exactly_once_key
52
+ # TQ.enqueue(idempotent_key, args, proc{|idempotent_key,args|
53
+ # Tx.idempotent(idempotent_key, args) do |args|
54
+ user = User.get(args[:user_id])
55
+ user.money += 5000
56
+ user.save
57
+ # end
58
+ # }.serialize)
59
+ end
60
+ end
61
+ describe "(7) base_transaction" do
62
+ base_tx_key = TxFlag.generate_key(@parent.key, unique_value)
63
+ Tx.base_transaction(args, procA, procB) do
64
+ # Tx.atomic{|tx1|
65
+ # yield(args){
66
+ # (procA)
67
+ a = get(args[:userA_key])
68
+ a.money -= args[:ampunt]
69
+ a.save
70
+ # }
71
+ # exactly_once_key = ...
72
+ # Tx.exactly_once_key(exactly_once_key, args){
73
+ # (procB)
74
+ b = get(args[:userB_key])
75
+ b.money += args[:ampunt]
76
+ b.save
77
+ # }
78
+ # }
79
+ end
80
+ end
81
+ end
82
+ =end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tiny_ds
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Takeru Sasaki
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-06 00:00:00 +09:00
12
+ date: 2010-01-30 00:00:00 +09:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -36,24 +36,18 @@ files:
36
36
  - README.rdoc
37
37
  - Rakefile
38
38
  - spec/basic_spec.rb
39
+ - spec/gae_spec.rb
39
40
  - spec/spec_helper.rb
40
- - spec/spec_helper.rb~
41
+ - spec/tx_spec.rb
41
42
  - lib/tiny_ds/base.rb
42
- - lib/tiny_ds/base.rb~
43
43
  - lib/tiny_ds/base_tx.rb
44
- - lib/tiny_ds/base_tx.rb~
45
44
  - lib/tiny_ds/low_ds.rb
46
- - lib/tiny_ds/low_ds.rb~
47
45
  - lib/tiny_ds/property_definition.rb
48
- - lib/tiny_ds/property_definition.rb~
49
46
  - lib/tiny_ds/query.rb
50
- - lib/tiny_ds/query.rb~
47
+ - lib/tiny_ds/transaction.rb
51
48
  - lib/tiny_ds/validations.rb
52
- - lib/tiny_ds/validations.rb~
53
49
  - lib/tiny_ds/version.rb
54
50
  - lib/tiny_ds.rb
55
- - lib/tiny_ds.rb~
56
- - lib/tinyds.rb~
57
51
  has_rdoc: true
58
52
  homepage: http://github.com/takeru/tiny_ds
59
53
  licenses: []
@@ -71,9 +65,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
71
65
  version:
72
66
  required_rubygems_version: !ruby/object:Gem::Requirement
73
67
  requirements:
74
- - - ">="
68
+ - - ">"
75
69
  - !ruby/object:Gem::Version
76
- version: "0"
70
+ version: 1.3.1
77
71
  version:
78
72
  requirements: []
79
73
 
data/lib/tiny_ds/base.rb~ DELETED
@@ -1,115 +0,0 @@
1
- =begin
2
- special pname
3
- - key
4
- - parent
5
- - id
6
- - name
7
- =end
8
-
9
-
10
- class ModelBase
11
- @@properties = {}
12
- def self.property(pname, ptype, opts={})
13
- pname = pname.to_sym
14
- if @@properties[pname]
15
- raise "duplicated pname=#{pname}"
16
- end
17
- @@properties[pname] = Property.new(pname, ptype, opts)
18
- end
19
-
20
- # kind-string of entity
21
- def self.kind
22
- name
23
- end
24
-
25
- def self.tx(retries=0, &block)
26
- transaction{
27
- yield(block)
28
- }
29
- end
30
-
31
- # Foo.create(attrs
32
- def self.create(attrs={})
33
- m = new(attrs)
34
- m.save
35
- m
36
- end
37
-
38
- # Foo.new
39
- def initialize(attrs={})
40
- attributes = attrs
41
- end
42
-
43
- def self.new_from_entity(entity)
44
- m = new()
45
- end
46
-
47
- # foo.save
48
- def save
49
- _save
50
- end
51
-
52
- def _save
53
- end
54
-
55
- # Foo.get(key)
56
- def self.get(key)
57
- if key.kind_of?(String)
58
- key = LowDS::KeyFactory.stringToKey(key)
59
- end
60
- raise "invalid key=#{key}" unless key.kind_of?(AppEngine::Datastore::Key)
61
- self.new_from_entity(LowDS.get(key, :kind=>self.kind))
62
- end
63
-
64
- def self.find(*args)
65
-
66
- end
67
-
68
- # foo.destroy
69
- def destroy
70
- end
71
-
72
- def attributes=(attrs)
73
- # TODO key, parent, id, name
74
-
75
- attrs.each do |k,v|
76
- prop = @@properties[k.to_sym]
77
- prop.value = v
78
- end
79
- nil
80
- end
81
-
82
- def method_missing(m, *args)
83
- prop, is_set = if m =~ /(.+)=$/
84
- [@@properties[$1.to_sym], true]
85
- else
86
- [@@properties[m.to_sym], false]
87
- end
88
- if prop
89
- # TODO define method.
90
- if is_set
91
- raise if args.size!=1
92
- prop.value = args.first
93
- else
94
- raise if args.size!=0
95
- prop.value
96
- end
97
- else
98
- super(m, *args)
99
- end
100
- end
101
-
102
- class Property
103
- def initialize(pname, ptype, opts)
104
- @pname = pname
105
- @ptype = ptype
106
- @opts = opts
107
- end
108
- def value=(v)
109
- # check_type or cast
110
- @value = v
111
- end
112
- attr_reader :value
113
- end
114
- end
115
-
@@ -1,180 +0,0 @@
1
- =begin
2
- 使い方
3
- class TxMoveUserPoint < BaseTx
4
- def src_phase(src, args)
5
- src[:point] -= args[:amount]
6
- end
7
- def dest_phase(dest, args)
8
- dest[:point] += args[:amount]
9
- end
10
- end
11
-
12
- u1 = User.create(:point=>1000)
13
- => #<User @sid=21 @name=nil @message=nil @point=1000 @created_at=Thu, 17 Dec 2009 21:14:29 +0900 @updated_at=Thu, 17 Dec 2009 21:14:29 +0900>
14
-
15
- u2 = User.create(:point=>1000)
16
- => #<User @sid=22 @name=nil @message=nil @point=1000 @created_at=Thu, 17 Dec 2009 21:14:33 +0900 @updated_at=Thu, 17 Dec 2009 21:14:33 +0900>
17
-
18
- TxMoveUserPoint.exec(u1.__entity__, u2.__entity__, :amount=>200)
19
- => true
20
-
21
- u1.reload
22
- => #<User @sid=21 @name=nil @message=nil @point=800 @created_at=Thu, 17 Dec 2009 21:14:29 +0900 @updated_at=Thu, 17 Dec 2009 21:14:29 +0900>
23
-
24
- u2.reload
25
- => #<User @sid=22 @name=nil @message=nil @point=1200 @created_at=Thu, 17 Dec 2009 21:14:33 +0900 @updated_at=Thu, 17 Dec 2009 21:14:33 +0900>
26
-
27
- TxMoveUserPoint.roll_forward_all
28
- =end
29
- # memo デプロイ時に未完了のトランザクションがあってトランザクション定義がかわったら?version???
30
-
31
- class BaseTx
32
- LowDS = TinyDS::LowDS
33
-
34
- def src_phase(src, args)
35
- raise "no impl."
36
- end
37
- def dest_phase(dest, args)
38
- raise "no impl."
39
- end
40
- def self.tx_kind
41
- name
42
- end
43
- def roll_forward_retries_limit
44
- 100
45
- end
46
- attr_reader :tx_key
47
-
48
- # トランザクション実行
49
- # : src_phase dest_phase
50
- # raised : failed ----
51
- # false : OK failed
52
- # true : OK OK
53
- def self.exec(src, dest, args={})
54
- tx = new
55
- tx.create_tx(src, dest, args)
56
- begin
57
- tx.roll_forward
58
- rescue AppEngine::Datastore::TransactionFailed => e
59
- return false
60
- end
61
- return true
62
- end
63
-
64
- def self.roll_forward_all(limit=50)
65
- pending_tx_query.each(:limit=>limit) do |tx_ent|
66
- tx = new
67
- tx.restore_tx(tx_ent)
68
- begin
69
- tx.roll_forward
70
- rescue => e
71
- # logger.warn("roll_forward failed. tx=[#{tx.tx_key}] e=[#{e.inspect}]")
72
- end
73
- end
74
- nil
75
- end
76
-
77
- def self.pending_tx_query(status="pending", dire=:asc) # pending/done/failed
78
- direction = dire==:desc ? AppEngine::Datastore::Query::DESCENDING : AppEngine::Datastore::Query::ASCENDING
79
- AppEngine::Datastore::Query.new("TxSrc").
80
- filter(:tx_kind, AppEngine::Datastore::Query::EQUAL, tx_kind).
81
- filter(:status, AppEngine::Datastore::Query::EQUAL, status).
82
- sort(:created_at, direction)
83
- end
84
-
85
- # doneなTxSrcと対応するTxDoneを削除する
86
- # def self.delete_done_tx
87
- # end
88
-
89
- require "benchmark"
90
- def create_tx(src, dest, args)
91
- #RAILS_DEFAULT_LOGGER.info ["create_tx", Benchmark.measure{
92
- _create_tx(src, dest, args)
93
- #}].inspect
94
- end
95
- def roll_forward
96
- #RAILS_DEFAULT_LOGGER.info ["roll_forward", Benchmark.measure{
97
- _roll_forward
98
- #}].inspect
99
- end
100
-
101
- # トランザクション前半
102
- # TODO TxIDを指定できるようにする。TxSrc#key.nameにTxIDを指定して重複実行防止
103
- def _create_tx(src, dest, args)
104
- tx_ent = nil
105
- ds_transaction{
106
- src = src.class.get(src.key)
107
- src_phase(src, args)
108
- src.save
109
-
110
- attrs = {
111
- :tx_kind => self.class.tx_kind,
112
- :dest_key => dest.key.to_s,
113
- :status => "pending",
114
- :roll_forward_failed_count => 0,
115
- :args => args.to_yaml,
116
- :created_at => Time.now
117
- }
118
- tx_ent = LowDS.create("TxSrc", attrs, :parent=>src.entity) # srcがparent, tx_entがchild
119
- # COMMIT:「srcの処理(=src_phase)、TxSrc作成」
120
- }
121
- @tx_key = tx_ent.key.to_s
122
- nil
123
- end
124
-
125
- def restore_tx(tx_ent)
126
- @tx_key = tx_ent.key.to_s
127
- nil
128
- end
129
-
130
- # トランザクション後半
131
- def _roll_forward
132
- tx_ent = LowDS.get(@tx_key, :kind=>"TxSrc")
133
-
134
- ds_transaction{
135
- dest_key = LowDS::KeyFactory.stringToKey(tx_ent[:dest_key])
136
- dest = dest_key.kind.constantize.get(dest_key)
137
- done_name = "TxDone_#{@tx_key}"
138
- done_key = LowDS::KeyFactory.createKey(dest.key, "TxDone", done_name)
139
- begin
140
- LowDS.get(done_key, "TxDone")
141
- # なにもしない : TxDoneが存在しているということはdest_phaseは処理済み
142
- rescue AppEngine::Datastore::EntityNotFound => e
143
- # TxDoneが無い→dest_phaseが未実行
144
- attrs = {:done_at=>Time.now}
145
- done_ent = LowDS.create("TxDone", attrs, :parent=>dest.entity, :name=>done_name)
146
- dest_phase(dest, YAML.load(tx_ent[:args])) # destの処理を実行
147
- dest.save
148
- end
149
- # memo: done_keyが同じTxDoneをcommitしようとするとTransactionFailedになるはず→dest_phaseもキャンセル
150
- # COMMIT:「destの処理(=dest_phase)、TxDone作成」
151
- }
152
-
153
- # TxSrc#statusをdoneに
154
- ds_transaction{
155
- tx_ent = LowDS.get(@tx_key, :kind=>"TxSrc")
156
- if tx_ent[:status]=="pending"
157
- tx_ent[:status] = "done"
158
- tx_ent[:done_at] = Time.now
159
- LowDS.save(tx_ent)
160
- end
161
- }
162
- return true
163
- rescue
164
- ds_transaction{
165
- tx_ent = LowDS.get(@tx_key, :kind=>"TxSrc")
166
- tx_ent[:roll_forward_failed_count] += 1
167
- if roll_forward_retries_limit < tx_ent[:roll_forward_failed_count]
168
- tx_ent[:status] = "failed"
169
- end
170
- LowDS.save(tx_ent)
171
- }
172
- return false
173
- end
174
-
175
- # EntityGroup単位のtransaction
176
- def ds_transaction(&block)
177
- retries = 3
178
- AppEngine::Datastore.transaction(retries, &block)
179
- end
180
- end
@@ -1,80 +0,0 @@
1
- module LowDS
2
- KeyFactory = com.google.appengine.api.datastore.KeyFactory
3
-
4
- # create
5
- def self.build(kind, attrs, opts={})
6
- raise "invalid opts=#{opts.inspect}" if [:id, :name, :key].collect{|k| opts[k] }.compact.size >= 2
7
- raise ":id must be Integer" if opts[:id] && !opts[:id].kind_of?(Integer)
8
- raise ":name must be String" if opts[:name] && !opts[:name].kind_of?(String)
9
- raise ":key must be Key or String" if opts[:key] && !(opts[:key].kind_of?(String) || opts[:key].kind_of?(AppEngine::Datastore::Key))
10
- name_or_id = opts[:name] || opts[:id] # name(String) or id(Integer)
11
- key = opts[:key].kind_of?(String) ? KeyFactory.stringToKey(parent) : opts[:key]
12
- parent = opts[:parent]
13
- ent = if key
14
- AppEngine::Datastore::Entity.new(key)
15
- elsif parent
16
- parent_key = case parent
17
- when AppEngine::Datastore::Entity; parent.key
18
- when AppEngine::Datastore::Key; parent
19
- when String; KeyFactory.stringToKey(parent)
20
- else raise "invalid parent type parent=[#{parent.inspect}]"
21
- end
22
- if name_or_id
23
- new_key = KeyFactory.createKey(parent_key, kind, name_or_id)
24
- AppEngine::Datastore::Entity.new(new_key)
25
- else
26
- AppEngine::Datastore::Entity.new(kind, parent_key)
27
- end
28
- else
29
- if name_or_id
30
- new_key = KeyFactory.createKey(kind, name_or_id)
31
- AppEngine::Datastore::Entity.new(new_key)
32
- else
33
- AppEngine::Datastore::Entity.new(kind)
34
- end
35
- end
36
- attrs.each do |k,v|
37
- ent[k] = v
38
- end
39
- ent
40
- end
41
-
42
- def self.create(kind, attrs, opts={})
43
- ent = build(kind, attrs, opts)
44
- AppEngine::Datastore.put(ent)
45
- ent
46
- end
47
-
48
- # get by key
49
- def self.get(key, opts={})
50
- key = case key
51
- when AppEngine::Datastore::Key; key
52
- when String; KeyFactory.stringToKey(key)
53
- else raise "invalid key type key.class=[#{key.class}] key.inspect=[#{key.inspect}]"
54
- end
55
- ent = AppEngine::Datastore.get(key)
56
- if opts[:kind]
57
- raise "kind missmatch. #{ent.kind}!=#{opts[:kind]}" if ent.kind!=opts[:kind]
58
- end
59
- ent
60
- end
61
-
62
- # search
63
- def self.search
64
- # TODO
65
- end
66
-
67
- # update
68
- def self.save(ent)
69
- AppEngine::Datastore.put(ent)
70
- end
71
-
72
- # delete
73
- def self.delete(ent)
74
- AppEngine::Datastore.delete(ent.key)
75
- end
76
-
77
- def self.tx(retries=0, &block)
78
- AppEngine::Datastore.transaction(retries, &block)
79
- end
80
- end
@@ -1,54 +0,0 @@
1
- module TinyDS
2
- class PropertyDefinition
3
- def initialize(pname, ptype, opts)
4
- @pname = pname
5
- @ptype = ptype
6
- @opts = opts
7
- end
8
- def default_value
9
- if @opts.has_key?(:default)
10
- default = @opts[:default]
11
- case default
12
- when Proc
13
- default.call
14
- else
15
- default
16
- end
17
- end
18
- end
19
- def to_ds_value(v)
20
- return nil if v.nil?
21
- case @ptype
22
- when :string
23
- v.to_s
24
- when :integer
25
- v.to_i
26
- when :text
27
- com.google.appengine.api.datastore::Text.new(java.lang.String.new(v))
28
- when :time
29
- Time.parse(v.to_s)
30
- #when :list
31
- # raise "todo"
32
- else
33
- raise "unknown type @ptype=#{@ptype}"
34
- end
35
- end
36
- def to_ruby_value(ds_v)
37
- return nil if ds_v.nil?
38
- case @ptype
39
- when :string
40
- ds_v.to_s
41
- when :integer
42
- ds_v.to_i
43
- when :text
44
- ds_v.to_s
45
- when :time
46
- Time.parse(ds_v.to_s)
47
- #when :list
48
- # raise "todo"
49
- else
50
- raise "unknown type @ptype=#{@ptype}"
51
- end
52
- end
53
- end
54
- end