blodsband 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,77 @@
1
+
2
+ module Blodsband
3
+
4
+ class Riak
5
+
6
+ class Counter
7
+
8
+ #
9
+ # Create a concurrent counter.
10
+ #
11
+ # @param [Blodsband::Riak::Bucket] bucket the bucket this counter lives in.
12
+ # @param [String] key the key for this counter.
13
+ #
14
+ def initialize(bucket, key)
15
+ @bucket = bucket
16
+ @key = key
17
+ end
18
+
19
+ #
20
+ # Increment this counter.
21
+ #
22
+ # @param [Integer] step the amount to increment the counter.
23
+ #
24
+ # @return [Integer] the new value for this counter. Note that there is no guarantee whatsoever as to at what point this value is true. Only that it was true at some point.
25
+ #
26
+ # @raise [RuntimerError] if step is < 0
27
+ #
28
+ def inc(step = 1)
29
+ raise RuntimeError.new("#{self}.inc(..) only allows positive values") if step < 0
30
+ current = get_resolved
31
+ current["last"] = current["last"] + current["step"]
32
+ current["step"] = step
33
+ val(get_resolved(@bucket.put(@key, current, :riak_params => {:readbody => true})))
34
+ end
35
+
36
+ #
37
+ # Get the value of this counter.
38
+ #
39
+ # @param [Blodsband::Riak::Result] current a {Blodsband::Riak::Response} defining the last known value (possibly with a gazillion siblings).
40
+ #
41
+ # @return [Integer] the current value of the counter. Note that there is no guarantee whatsoever as to when this value was true. Only that it was true at some point.
42
+ #
43
+ def val(current = nil)
44
+ current = get_resolved if current.nil?
45
+ return current["last"] + current["step"]
46
+ end
47
+
48
+ private
49
+
50
+ def get_resolved(current = nil)
51
+ current = @bucket.get(@key, :ary => true) if current.nil?
52
+ if current.empty?
53
+ current = {
54
+ "step" => 0,
55
+ "last" => 0
56
+ }
57
+ return get_resolved(@bucket.put(@key, current, :riak_params => {:readbody => true}))
58
+ else
59
+ last = 1 << 256
60
+ step = 0
61
+ current.each do |v|
62
+ step += v["step"]
63
+ last = v["last"] if v["last"] < last
64
+ end
65
+ sum = {
66
+ "step" => step,
67
+ "last" => last
68
+ }
69
+ return current.copy_to(sum)
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+
77
+ end
@@ -0,0 +1,880 @@
1
+
2
+ module Blodsband
3
+
4
+ class Riak
5
+
6
+ #
7
+ # A concurrent linked list.
8
+ #
9
+ class List
10
+
11
+ #
12
+ # An error signaling that saving a piece of data failed because it has
13
+ # been concurrently updated elsewhere.
14
+ #
15
+ class ConcurrentUpdateError < RuntimeError
16
+ #
17
+ # [Object] The actor that was concurrently updated.
18
+ #
19
+ attr_reader :actor
20
+ def initialize(actor, message)
21
+ super(message)
22
+ @actor = actor
23
+ end
24
+ end
25
+
26
+ #
27
+ # An error signaling that performing an operation failed because one or more
28
+ # of the actors in the operation have been deleted.
29
+ #
30
+ class ActorDeletedError < RuntimeError
31
+ #
32
+ # [Object] The actor that was deleted.
33
+ #
34
+ attr_reader :actor
35
+ def initialize(actor, message)
36
+ super(message)
37
+ @actor = actor
38
+ end
39
+ end
40
+
41
+ #
42
+ # The list element.
43
+ #
44
+ class Element
45
+
46
+ #
47
+ # The key in Riak for this element.
48
+ #
49
+ attr_reader :key
50
+ #
51
+ # The {Blodsband::Riak::List} this element belongs to.
52
+ #
53
+ attr_reader :list
54
+
55
+ #
56
+ # Find an element.
57
+ #
58
+ # @param [Blodsband::Riak::List] list the list the element belongs to.
59
+ # @param [String] key the key for this element.
60
+ #
61
+ # @return [Blodsband::Riak::Element] the element at the given key.
62
+ #
63
+ def self.find(list, key)
64
+ rval = Element.new
65
+ rval.instance_eval do
66
+ @list = list
67
+ @key = key
68
+ end
69
+ rval
70
+ end
71
+
72
+ #
73
+ # @private
74
+ #
75
+ # Create an element.
76
+ #
77
+ # @param [Blodsband::Riak::List] list the list the element shall belong to.
78
+ # @param [Object] value the value to put in the element.
79
+ #
80
+ # @return [Blodsband::Riak::Element] a new (unsaved) element with the given value.
81
+ #
82
+ def self.create(list, value)
83
+ rval = Element.new
84
+ rval.instance_eval do
85
+ @list = list
86
+ @key = rand(1 << 256).to_s(36)
87
+ @value = value
88
+ class << @value
89
+ include Response
90
+ end
91
+ @value.instance_eval do
92
+ @meta = (@meta || {}).merge("list-key" => list.key)
93
+ end
94
+ end
95
+ rval.save
96
+ rval
97
+ end
98
+
99
+ #
100
+ # @return [true, false] whether the element exists.
101
+ #
102
+ def exists?
103
+ reload
104
+ !key.nil? && !value.nil?
105
+ end
106
+
107
+ #
108
+ # @return [String] a {::String} representation of the element.
109
+ #
110
+ def to_s
111
+ if exists?
112
+ "#{self.previous} => #{self.class}:#{key}@#{list.key} #{value.inspect} => #{self.next}"
113
+ else
114
+ "#{self.class}:#{key}@#{list.key}"
115
+ end
116
+ end
117
+
118
+ #
119
+ # @param [Object] o the new value to set in this element.
120
+ #
121
+ # @note Does not lock the parent list, since changing the element value is independent
122
+ # of structural list changes. Instead both this and structural list changes just retry
123
+ # on {Blodsband::Riak::List::ConcurrentUpdateError}.
124
+ #
125
+ # @return [Object] the new value.
126
+ #
127
+ def value=(o)
128
+ begin
129
+ old_value = value
130
+ @value = value.copy_to(o)
131
+ value.instance_eval do
132
+ @meta = old_value.meta
133
+ end
134
+ save
135
+ value
136
+ rescue ConcurrentUpdateError => e
137
+ if !exists?
138
+ raise ActorDeletedError.new(self, to_s)
139
+ else
140
+ retry
141
+ end
142
+ end
143
+ end
144
+
145
+ #
146
+ # @return [Object] the value of this element.
147
+ #
148
+ def value
149
+ @value ||= list.bucket.get(key, :unique => true)
150
+ end
151
+
152
+ #
153
+ # Prepend a value before this element in the list.
154
+ #
155
+ # @param [Object] value the value to prepend.
156
+ #
157
+ # @return [Object] the value inserted.
158
+ #
159
+ def prepend(value)
160
+ if exists?
161
+ while !list.prepend_element_before(self, Element.create(list, value))
162
+ list.reload
163
+ reload
164
+ raise ActorDeletedError.new(self, to_s) unless exists?
165
+ raise ActorDeletedError.new(list, list.to_s) unless list.exists?
166
+ end
167
+ else
168
+ raise ActorDeletedError.new(self, to_s)
169
+ end
170
+ end
171
+
172
+ #
173
+ # Delete this element.
174
+ #
175
+ def delete
176
+ if exists?
177
+ while !list.delete_element(self)
178
+ list.reload
179
+ reload
180
+ break unless exists?
181
+ raise ActorDeletedError.new(list, list.to_s) unless list.exists?
182
+ end
183
+ end
184
+ end
185
+
186
+ #
187
+ # Append a value after this element in the list.
188
+ #
189
+ # @param [Object] value the value to append.
190
+ #
191
+ # @return [Object] the value appended.
192
+ #
193
+ def append(value)
194
+ if exists?
195
+ while !list.append_element_after(self, Element.create(list, value))
196
+ list.reload
197
+ reload
198
+ raise ActorDeletedError.new(self, to_s) unless exists?
199
+ raise ActorDeletedError.new(list, list.to_s) unless list.exists?
200
+ end
201
+ else
202
+ raise ActorDeletedError.new(self, to_s)
203
+ end
204
+ end
205
+
206
+ #
207
+ # @private
208
+ #
209
+ # Save this element.
210
+ #
211
+ # @raise [Blodsband::Riak::List::ConcurrentUpdateError] if the element has already been updated elsewhere.
212
+ #
213
+ def save
214
+ curr = @value
215
+ @value = list.bucket.cas(key, value, value.vclock)
216
+ if @value.nil?
217
+ raise ConcurrentUpdateError.new(self, to_s)
218
+ else
219
+ #STDERR.puts("#{$$} succeeded saving #{curr} (#{curr.meta})")
220
+ end
221
+ end
222
+
223
+ #
224
+ # @private
225
+ #
226
+ # Make sure this element is reloaded from Riak.
227
+ #
228
+ def reload
229
+ @value = nil
230
+ end
231
+
232
+ #
233
+ # @private
234
+ #
235
+ # @return [String] the key to the next element in the list.
236
+ #
237
+ def next
238
+ value.meta["list-next"]
239
+ end
240
+
241
+ #
242
+ # @private
243
+ #
244
+ # @return [Blodsband::Riak::List::Element] the next element or nil.
245
+ #
246
+ def next_element
247
+ Element.find(list, self.next)
248
+ end
249
+
250
+ #
251
+ # @private
252
+ #
253
+ # @return [String] the key to the previous element in the list.
254
+ #
255
+ def previous
256
+ value.meta["list-previous"]
257
+ end
258
+
259
+ #
260
+ # @private
261
+ #
262
+ # @return [Blodsband::Riak::List::Element] the previous element or nil.
263
+ #
264
+ def previous_element
265
+ Element.find(list, self.previous)
266
+ end
267
+
268
+ #
269
+ # @private
270
+ #
271
+ def set_pointers(list_version, previous_key, next_key)
272
+ begin
273
+ list_doc = list.bucket.get(list.key)
274
+ if list_doc.nil? || list_doc["version"] == list_version
275
+ if previous_key.nil?
276
+ value.meta.delete("list-previous")
277
+ elsif previous_key != ":unchanged"
278
+ value.meta["list-previous"] = previous_key
279
+ end
280
+ if next_key.nil?
281
+ value.meta.delete("list-next")
282
+ elsif next_key != ":unchanged"
283
+ value.meta["list-next"] = next_key
284
+ end
285
+ save
286
+ end
287
+ rescue ConcurrentUpdateError => e
288
+ retry if exists?
289
+ end
290
+ end
291
+
292
+ end
293
+
294
+ #
295
+ # The key of this list in Riak.
296
+ #
297
+ attr_reader :key
298
+ #
299
+ # The {Blodsband::Riak::Bucket} this list lives in.
300
+ #
301
+ attr_reader :bucket
302
+
303
+ #
304
+ # Access a list in a bucket.
305
+ #
306
+ # @param [Blodsband::Riak::Bucket] the bucket the list lives in.
307
+ # @param [String] key the key of the list.
308
+ #
309
+ def initialize(bucket, key)
310
+ @bucket = bucket
311
+ @key = key
312
+ end
313
+
314
+ #
315
+ # @return [String] a string representation of the list.
316
+ #
317
+ def to_s
318
+ "#{self.class}:#{key}"
319
+ end
320
+
321
+ #
322
+ # @return [true, false] whether this list is empty.
323
+ #
324
+ def empty?
325
+ size == 0
326
+ end
327
+
328
+ #
329
+ # @return [true, false] whether this list exists in Riak.
330
+ #
331
+ def exists?
332
+ !key.nil? && !bucket.get(key).nil?
333
+ end
334
+
335
+ #
336
+ # @return [Integer] the size of this list.
337
+ #
338
+ def size
339
+ document["size"] ||= 0
340
+ end
341
+
342
+ #
343
+ # @return [Array] this list as a ruby {::Array}.
344
+ #
345
+ def to_a
346
+ rval = []
347
+ each do |e|
348
+ rval << e
349
+ end
350
+ rval
351
+ end
352
+
353
+ #
354
+ # @return [Object] the last element of this list after having removed it.
355
+ #
356
+ def pop
357
+ if empty?
358
+ nil
359
+ else
360
+ e = Element.find(self, document["tail"])
361
+ rval = e.value
362
+ e.delete
363
+ rval
364
+ end
365
+ end
366
+
367
+ #
368
+ # @return [Object] the first element of this list after having removed it.
369
+ #
370
+ def shift
371
+ if empty?
372
+ nil
373
+ else
374
+ e = Element.find(self, document["head"])
375
+ rval = e.value
376
+ e.delete
377
+ rval
378
+ end
379
+ end
380
+
381
+ #
382
+ # @return [Object] the first element of this list.
383
+ #
384
+ def first
385
+ if empty?
386
+ nil
387
+ else
388
+ Element.find(self, document["head"]).value
389
+ end
390
+ end
391
+
392
+ #
393
+ # @return [Object] the last element of this list.
394
+ #
395
+ def last
396
+ if empty?
397
+ nil
398
+ else
399
+ Element.find(self, document["tail"]).value
400
+ end
401
+ end
402
+
403
+ #
404
+ # @private
405
+ #
406
+ # Save this list.
407
+ #
408
+ # @raise [Blodsband::Riak::ConcurrentUpdateError] if this list has already been updated elswhere.
409
+ #
410
+ def save
411
+ curr = document
412
+ @document = bucket.cas(key, document, document.vclock)
413
+ if @document.nil?
414
+ raise ConcurrentUpdateError.new(self, to_s)
415
+ else
416
+ #STDERR.puts("#{$$} succeeded saving #{@document.inspect}")
417
+ end
418
+ end
419
+
420
+ def stack_each(current, &block)
421
+ stack = []
422
+ while current
423
+ if current.exists?
424
+ block.call(current.value, current)
425
+ if current.next
426
+ stack << current
427
+ if stack.include?(current.next_element)
428
+ raise "Loop detected! Wtf? #{stack.inspect}"
429
+ else
430
+ current = current.next_element
431
+ end
432
+ else
433
+ current = nil
434
+ end
435
+ else
436
+ if stack.empty?
437
+ return
438
+ else
439
+ while !stack.empty? && !stack.last.next
440
+ stack.pop
441
+ end
442
+ if stack.empty?
443
+ current = nil
444
+ else
445
+ current = stack.last.next_element
446
+ end
447
+ end
448
+ end
449
+ end
450
+ end
451
+
452
+ #
453
+ # Execute code on each element in the list.
454
+ #
455
+ # @param [Block |value, element|] block the code to execute.
456
+ #
457
+ def each(&block)
458
+ begin
459
+ element = nil
460
+ while size > 0 && !(element = Element.find(self, document["head"])).exists?
461
+ reload
462
+ end
463
+ stack_each(element, &block) if size > 0
464
+ rescue ActorDeletedError => e
465
+ if e.actor == self
466
+ raise e
467
+ else
468
+ retry
469
+ end
470
+ end
471
+ end
472
+
473
+ #
474
+ # Append to the list.
475
+ #
476
+ # @param [Object] value the value to append.
477
+ #
478
+ # @return [Object] the appended value.
479
+ #
480
+ def append(value)
481
+ append_and_get_element(value).value
482
+ end
483
+
484
+ alias :<< :append
485
+
486
+ #
487
+ # @private
488
+ #
489
+ # Append to the list.
490
+ #
491
+ # @param (see #append)
492
+ #
493
+ # @return [Blodsband::Riak::List::Element] the appended element.
494
+ #
495
+ def append_and_get_element(value)
496
+ new_node = Element.create(self, value)
497
+ append_element(new_node)
498
+ new_node
499
+ end
500
+
501
+ #
502
+ # Prepend to the list.
503
+ #
504
+ # @param [Object] value the value to prepend.
505
+ #
506
+ # @return [Object] the prepended value.
507
+ #
508
+ def prepend(value)
509
+ new_node = Element.create(self, value)
510
+ prepend_element(new_node)
511
+ value
512
+ end
513
+
514
+ #
515
+ # @private
516
+ #
517
+ def prepend_element_before(element, new_node)
518
+ add_element(element, new_node, :execute_prepend_element_before, "prepend #{new_node.value} before #{element.value}")
519
+ end
520
+
521
+ #
522
+ # @private
523
+ #
524
+ def append_element_after(element, new_node)
525
+ add_element(element, new_node, :execute_append_element_after, "append #{new_node.value} after #{element.value}")
526
+ end
527
+
528
+ #
529
+ # @private
530
+ #
531
+ # Delete an element.
532
+ #
533
+ # @param [Blodsband::Riak::List::Element] element the element to delete.
534
+ #
535
+ def delete_element(element)
536
+ begin
537
+ backlog << [:execute_delete_element,
538
+ element.key,
539
+ "delete #{element.value}"]
540
+
541
+ save
542
+
543
+ begin
544
+ rollforward
545
+ rescue ConcurrentUpdateError => e
546
+ ensure
547
+ return true
548
+ end
549
+ rescue ConcurrentUpdateError => e
550
+ if !exists?
551
+ raise ActorDeletedError.new(self, to_s)
552
+ elsif element.exists?
553
+ false
554
+ end
555
+ end
556
+ end
557
+
558
+ #
559
+ # @private
560
+ #
561
+ # Make sure this list gets reloaded from Riak.
562
+ #
563
+ def reload
564
+ @document = nil
565
+ end
566
+
567
+ #
568
+ # @private
569
+ #
570
+ # Validate that this list is consistent.
571
+ #
572
+ def validate
573
+ d = bucket.get(key)
574
+ s = 0
575
+ p = nil
576
+ pe = nil
577
+ c = d["head"]
578
+ seen = []
579
+ while c
580
+ seen << c
581
+ e = bucket.get(c)
582
+ raise "#{e} (#{e.meta.inspect}) doesn't point back to #{pe} (#{pe.meta.inspect})" if p && e.meta["list-previous"] != p
583
+ p = c
584
+ pe = e
585
+ s += 1
586
+ c = e.meta["list-next"]
587
+ raise "#{e} (#{e.meta.inspect}) is the the beginning of a loop: #{seen.inspect}" if seen.include?(c)
588
+ seen << c
589
+ end
590
+ raise "#{d.inspect}['tail'] doesn't point to the de facto tail #{pe}" unless d["tail"] == p
591
+ raise "#{d.inspect}['size'] doesn't show the de facto size #{s}" unless s == d["size"]
592
+ end
593
+
594
+ protected
595
+
596
+ def ok_to_add?(element)
597
+ true
598
+ end
599
+
600
+ def backlog_add(element)
601
+ end
602
+
603
+ def backlog_delete(element)
604
+ end
605
+
606
+ def backlog
607
+ document["backlog"] ||= []
608
+ document["backlog"]
609
+ end
610
+
611
+ private
612
+
613
+ def add_element(element, new_node, method, desc)
614
+ begin
615
+ backlog << [method,
616
+ element.key,
617
+ new_node.key,
618
+ desc]
619
+
620
+ save
621
+
622
+ begin
623
+ rollforward
624
+ rescue ConcurrentUpdateError => e
625
+ ensure
626
+ return true
627
+ end
628
+ rescue ConcurrentUpdateError => e
629
+ if !exists?
630
+ raise ActorDeletedError.new(self, to_s)
631
+ elsif !element.exists?
632
+ raise ActorDeletedError.new(element, element.to_s)
633
+ else
634
+ return false
635
+ end
636
+ end
637
+ end
638
+
639
+ def set_element_pointers(v, element_key, previous_key, next_key, desc)
640
+ Element.find(self, element_key).set_pointers(v, previous_key, next_key)
641
+ end
642
+
643
+ def actually_delete_element(element_key, desc)
644
+ bucket.delete(element_key)
645
+ end
646
+
647
+ def execute_delete_element(element_key, desc)
648
+ element = Element.find(self, element_key)
649
+ if element.exists?
650
+ previous_element = element.previous_element
651
+ next_element = element.next_element
652
+ #STDERR.puts("#{$$} chain:\n #{previous_element}\n #{element}\n #{next_element}")
653
+
654
+ document["version"] = version + 1
655
+
656
+ backlog << [:set_element_pointers,
657
+ version,
658
+ next_element.key,
659
+ element.previous,
660
+ ":unchanged",
661
+ desc] if next_element.exists?
662
+
663
+ backlog << [:set_element_pointers,
664
+ version,
665
+ previous_element.key,
666
+ ":unchanged",
667
+ next_element.key,
668
+ desc] if previous_element.exists?
669
+
670
+ backlog << [:actually_delete_element,
671
+ element.key,
672
+ desc]
673
+
674
+ backlog_delete(element)
675
+
676
+ document["head"] = element.next if element.previous.nil?
677
+ document["tail"] = element.previous if element.next.nil?
678
+ document["size"] -= 1
679
+ end
680
+ end
681
+
682
+ def execute_prepend_element_before(element_key, new_key, desc)
683
+ element = Element.find(self, element_key)
684
+ if element.exists?
685
+ new_element = Element.find(self, new_key)
686
+ if ok_to_add?(new_element)
687
+ previous_element = element.previous_element
688
+ #STDERR.puts("#{$$} chain:\n #{previous_element}\n #{new_element}\n #{element}")
689
+
690
+ document["version"] = version + 1
691
+
692
+ backlog << [:set_element_pointers,
693
+ version,
694
+ new_element.key,
695
+ previous_element.key,
696
+ element.key,
697
+ desc]
698
+ backlog << [:set_element_pointers,
699
+ version,
700
+ element.key,
701
+ new_element.key,
702
+ ":unchanged",
703
+ desc]
704
+ backlog << [:set_element_pointers,
705
+ version,
706
+ previous_element.key,
707
+ ":unchanged",
708
+ new_element.key,
709
+ desc] if previous_element.exists?
710
+
711
+ backlog_add(new_element)
712
+
713
+ document["head"] = new_key if element.previous.nil?
714
+ document["size"] = size + 1
715
+ else
716
+ bucket.delete(new_element.key)
717
+ end
718
+ end
719
+ end
720
+
721
+ def execute_append_element_after(element_key, new_key, desc)
722
+ element = Element.find(self, element_key)
723
+ if element.exists?
724
+ new_element = Element.find(self, new_key)
725
+ if ok_to_add?(new_element)
726
+ next_element = element.next_element
727
+ #STDERR.puts("#{$$} chain:\n #{element}\n #{new_element}\n #{next_element}")
728
+ document["version"] = version + 1
729
+
730
+ backlog << [:set_element_pointers,
731
+ version,
732
+ new_element.key,
733
+ element.key,
734
+ next_element.key,
735
+ desc]
736
+ backlog << [:set_element_pointers,
737
+ version,
738
+ element.key,
739
+ ":unchanged",
740
+ new_element.key,
741
+ desc]
742
+ backlog << [:set_element_pointers,
743
+ version,
744
+ next_element.key,
745
+ new_element.key,
746
+ ":unchanged",
747
+ desc] if next_element.exists?
748
+
749
+ backlog_add(new_element)
750
+
751
+ document["tail"] = new_key if element.next.nil?
752
+ document["size"] = size + 1
753
+ else
754
+ bucket.delete(new_element.key)
755
+ end
756
+ end
757
+ end
758
+
759
+ def prepend_element(new_node)
760
+ done = false
761
+ while !done
762
+ if empty?
763
+ done = set_first_element(new_node)
764
+ else
765
+ first_element = Element.find(self, document["head"])
766
+ if first_element.exists?
767
+ begin
768
+ done = prepend_element_before(first_element, new_node)
769
+ rescue ActorDeletedError => e
770
+ if e.actor != first_element
771
+ raise e
772
+ else
773
+ reload
774
+ end
775
+ end
776
+ else
777
+ reload
778
+ end
779
+ end
780
+ end
781
+ end
782
+
783
+ def append_element(new_node)
784
+ done = false
785
+ while !done
786
+ if empty?
787
+ done = set_first_element(new_node)
788
+ else
789
+ last_element = Element.find(self, document["tail"])
790
+ if last_element.exists?
791
+ begin
792
+ done = append_element_after(last_element, new_node)
793
+ rescue ActorDeletedError => e
794
+ if e.actor != last_element
795
+ raise e
796
+ else
797
+ reload
798
+ end
799
+ end
800
+ else
801
+ reload
802
+ end
803
+ end
804
+ end
805
+ end
806
+
807
+ def set_first_element(new_node)
808
+ begin
809
+ document["version"] = version + 1
810
+ new_node.set_pointers(version, nil, nil)
811
+ new_node.save
812
+ document["head"] = new_node.key
813
+ document["tail"] = new_node.key
814
+ document["size"] = 1
815
+ backlog_add(new_node)
816
+ save
817
+ begin
818
+ rollforward
819
+ rescue ConcurrentUpdateError => e
820
+ ensure
821
+ return true
822
+ end
823
+ rescue ConcurrentUpdateError => e
824
+ if !exists?
825
+ raise ActorDeletedError.new(self, to_s)
826
+ else
827
+ return false
828
+ end
829
+ end
830
+ end
831
+
832
+ def rollforward
833
+ while !backlog.empty?
834
+ begin
835
+ todo = backlog.clone
836
+ #STDERR.puts("#{$$} going to roll #{todo.inspect} forward")
837
+ copy = todo.clone
838
+ backlog.clear
839
+ while !todo.empty?
840
+ self.send(*(todo.shift))
841
+ end
842
+ save
843
+ #STDERR.puts("#{$$} rolled #{copy.inspect} forward")
844
+ rescue ConcurrentUpdateError => e
845
+ reload
846
+ retry unless (_document["backlog"] || []).empty?
847
+ end
848
+ end
849
+ end
850
+
851
+ def version
852
+ document["version"] || 0
853
+ end
854
+
855
+ def _document
856
+ if @document.nil?
857
+ @document = bucket.get(key, :unique => true)
858
+ if @document.nil?
859
+ @document = {}
860
+ class << @document
861
+ include Response
862
+ end
863
+ end
864
+ end
865
+ @document
866
+ end
867
+
868
+ def document
869
+ if @document.nil?
870
+ _document
871
+ rollforward
872
+ end
873
+ @document
874
+ end
875
+
876
+ end
877
+
878
+ end
879
+
880
+ end