Class: Riak::CRDT::TGCounter

Inherits:
Object
  • Object
show all
Defined in:
lib/crdt/tgcounter.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ TGCounter

Create a new Transaction GCounter

Parameters:

  • options (Hash)

    :actor [String]
    :history_length [Integer]
    



11
12
13
14
15
16
17
18
# File 'lib/crdt/tgcounter.rb', line 11

def initialize(options)
  self.actor = options[:actor]
  self.history_length = options[:history_length]
  self.counts = Hash.new()
  self.counts[self.actor] = Hash.new()
  self.counts[self.actor]["total"] = 0
  self.counts[self.actor]["txns"] = TransactionArray.new()
end

Instance Attribute Details

#actorObject

Returns the value of attribute actor.



3
4
5
# File 'lib/crdt/tgcounter.rb', line 3

def actor
  @actor
end

#countsObject

Returns the value of attribute counts.



3
4
5
# File 'lib/crdt/tgcounter.rb', line 3

def counts
  @counts
end

#history_lengthObject

Returns the value of attribute history_length.



3
4
5
# File 'lib/crdt/tgcounter.rb', line 3

def history_length
  @history_length
end

Class Method Details

.from_hash(h, options) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
# File 'lib/crdt/tgcounter.rb', line 38

def self.from_hash(h, options)
  gc = new(options)

  h['c'].each do |a, values|
    gc.counts[a] = Hash.new() unless gc.counts[a]
    gc.counts[a]["total"] = values["total"]
    gc.counts[a]["txns"] = TransactionArray.new(values["txns"])
  end

  return gc
end

.from_json(json, options) ⇒ Object



50
51
52
53
54
55
# File 'lib/crdt/tgcounter.rb', line 50

def self.from_json(json, options)
  h = JSON.parse json
  raise ArgumentError.new 'unexpected type field in JSON' unless h['type'] == 'TGCounter'

  from_hash(h, options)
end

Instance Method Details

#compress_historyObject

Compress this actor’s data based on history_length



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/crdt/tgcounter.rb', line 169

def compress_history()
  total = 0

  duplicates = self.duplicate_transactions()

  if self.counts[actor]["txns"].length > self.history_length
    to_delete = self.counts[actor]["txns"].length - self.history_length
    self.counts[actor]["txns"].arr.slice!(0..to_delete - 1).each do |arr|
      txn, val = arr
      total += val unless duplicates.member? txn
    end
  end

  self.counts[actor]["total"] += total
end

#duplicate_transactionsHash

Get unique list of all duplicate transactions for all actors other than self

Returns:

  • (Hash)


98
99
100
101
102
103
104
105
106
107
108
# File 'lib/crdt/tgcounter.rb', line 98

def duplicate_transactions()
  duplicates = Hash.new()

  self.duplicate_transactions_by_actor().each do |a, txns|
    txns.each do |txn, val|
      duplicates[txn] = val
    end
  end

  duplicates
end

#duplicate_transactions_by_actorHash

Get unique list of all duplicate transactions per actor other than self

Returns:

  • (Hash)


82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/crdt/tgcounter.rb', line 82

def duplicate_transactions_by_actor()
  actor_txns = Hash.new()

  my_transactions = self.unique_transactions(self.actor).keys

  self.counts.keys.each do |a|
    next if a == self.actor
    uniques = self.unique_transactions(a).keys
    actor_txns[a] = (my_transactions & uniques)
  end

  actor_txns
end

#has_transaction?(transaction) ⇒ Boolean

Returns:

  • (Boolean)


110
111
112
# File 'lib/crdt/tgcounter.rb', line 110

def has_transaction?(transaction)
  self.unique_transactions().keys.member?(transaction)
end

#increment(transaction, value) ⇒ Object

Increment this actor’s transaction array, overwriting if the value exists

Parameters:

  • transaction (String)
  • value (Integer)


60
61
62
# File 'lib/crdt/tgcounter.rb', line 60

def increment(transaction, value)
  self.counts[actor]["txns"][transaction] = value
end

#merge(other) ⇒ Object

Merge actor data from a sibling into self, additionally remove duplicate transactions and compress oldest transactions that exceed the :history_length param into actor’s total

Parameters:



130
131
132
133
134
# File 'lib/crdt/tgcounter.rb', line 130

def merge(other)
  self.merge_actors(other)
  self.remove_duplicates()
  self.compress_history()
end

#merge_actors(other) ⇒ Object

Combine all actors’ data



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/crdt/tgcounter.rb', line 137

def merge_actors(other)
  other.counts.each do |other_actor, other_values|
    if self.counts[other_actor]
      # Max of totals
      mine = self.counts[other_actor]["total"]
      self.counts[other_actor]["total"] = [mine, other_values["total"]].max

      # Max of unique transactions
      other_values["txns"].arr.each do |arr|
        other_txn, other_value = arr
        mine = (self.counts[other_actor]["txns"][other_txn]) ?
            self.counts[other_actor]["txns"][other_txn] : 0
        self.counts[other_actor]["txns"][other_txn] = [mine, other_value].max
      end
    else
      self.counts[other_actor] = other_values
    end
  end
end

#remove_duplicatesObject

Remove duplicate transactions if other actors have claimed them



158
159
160
161
162
163
164
165
166
# File 'lib/crdt/tgcounter.rb', line 158

def remove_duplicates()
  self.duplicate_transactions_by_actor().each do |a, txns|
    # Spaceship operator, if my actor is of greater value than theirs, skip because they should remove the dupe
    next if (self.actor <=> a) == 1
    txns.each do |txn|
      self.counts[self.actor]["txns"].delete(txn)
    end
  end
end

#to_hashObject



20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/crdt/tgcounter.rb', line 20

def to_hash
  c = Hash.new()
  self.counts.each do |a, values|
    c[a] = Hash.new()
    c[a]["total"] = values["total"]
    c[a]["txns"] = values["txns"].arr
  end

  {
      type: 'TGCounter',
      c: c
  }
end

#to_jsonObject



34
35
36
# File 'lib/crdt/tgcounter.rb', line 34

def to_json
  self.to_hash.to_json
end

#unique_transactions(for_actor = nil) ⇒ Hash

Get unique list of all transactions and values across all known actors, or optionally for a single actor

Parameters:

  • for_actor (String) (defaults to: nil)

Returns:

  • (Hash)


67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/crdt/tgcounter.rb', line 67

def unique_transactions(for_actor=nil)
  txns = Hash.new()

  self.counts.each do |a, values|
    next if for_actor && a != for_actor
    values["txns"].arr.each do |arr|
      txns[arr[0]] = arr[1]
    end
  end

  txns
end

#valueInteger

Sum of totals and currently tracked transactions

Returns:

  • (Integer)


116
117
118
119
120
121
122
123
124
# File 'lib/crdt/tgcounter.rb', line 116

def value()
  total = self.unique_transactions().values.inject(0, &:+)

  self.counts.values.each do |a|
    total += a["total"]
  end

  total
end