Class: Ethereum::DB::RefcountDB

Inherits:
Object
  • Object
show all
Includes:
BaseDB
Defined in:
lib/ethereum/db/refcount_db.rb

Constant Summary collapse

DEATH_ROW_OFFSET =
2**62
ZERO_ENCODED =
Utils.encode_int(0)
ONE_ENCODED =
Utils.encode_int(1)

Instance Attribute Summary collapse

Attributes included from BaseDB

#db

Instance Method Summary collapse

Constructor Details

#initialize(db) ⇒ RefcountDB

Returns a new instance of RefcountDB.



15
16
17
18
19
20
21
22
# File 'lib/ethereum/db/refcount_db.rb', line 15

def initialize(db)
  @db = db
  @journal = []
  @death_row = []
  @kv = @db.respond_to?(:kv) ? @db.kv : nil

  self.ttl = 500
end

Instance Attribute Details

#ttlObject

Returns the value of attribute ttl.



13
14
15
# File 'lib/ethereum/db/refcount_db.rb', line 13

def ttl
  @ttl
end

Instance Method Details

#cleanup(epoch) ⇒ Object

Kill nodes that are eligible to be killed, and remove the associated deathrow record. Also delete old journals.



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/ethereum/db/refcount_db.rb', line 86

def cleanup(epoch)
  rlp_nodes = @db.get("deathrow:#{epoch}") rescue RLP.encode([])
  death_row_nodes = RLP.decode rlp_nodes

  pruned = 0
  offset = DEATH_ROW_OFFSET + epoch

  death_row_nodes.each do |node_key|
    begin
      refcount, val = RLP.decode ref_get(node_key)
      if Utils.decode_int(refcount) == offset
        @db.delete ref_key(node_key)
        pruned += 1
      end
    rescue
      logger.debug "in cleanup: #{$!}"
    end
  end
  logger.debug "#{pruned} nodes successfully pruned"

  @db.delete "deathrow:#{epoch}" rescue nil
  @db.delete "journal:#{epoch - ttl}" rescue nil
end

#commitObject



168
169
170
# File 'lib/ethereum/db/refcount_db.rb', line 168

def commit
  @db.commit
end

#commit_refcount_changes(epoch) ⇒ Object

Commit changes to the journal and death row to the database.



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/ethereum/db/refcount_db.rb', line 113

def commit_refcount_changes(epoch)
  timeout_epoch = epoch + ttl
  death_row_nodes = RLP.decode(@db.get("deathrow:#{timeout_epoch}")) rescue []

  @death_row.each do |node_key|
    refcount, val = RLP.decode ref_get(node_key)
    if refcount == ZERO_ENCODED
      new_refcount = Utils.encode_int(DEATH_ROW_OFFSET + timeout_epoch)
      ref_put node_key, RLP.encode([new_refcount, val])
    end
  end

  unless @death_row.empty?
    logger.debug "#{@death_row.size} nodes marked for pruning during block #{timeout_epoch}"
  end

  death_row_nodes.concat @death_row
  @death_row = []
  @db.put "deathrow:#{timeout_epoch}", RLP.encode(death_row_nodes)

  journal = RLP.decode(@db.get("journal:#{epoch}")) rescue []
  journal.concat @journal
  @journal = []
  @db.put "journal:#{epoch}", RLP.encode(journal)
end

#dec_refcount(k) ⇒ Object Also known as: delete

Decrease the reference count associated with a key.

Raises:



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/ethereum/db/refcount_db.rb', line 53

def dec_refcount(k)
  node_object = RLP.decode ref_get(k)
  refcount = Utils.decode_int node_object[0]

  if logger.trace?
    logger.trace "decreasing #{Utils.encode_hex(k)} to #{refcount-1}"
  end

  raise AssertError, "refcount must be greater than zero!" unless refcount > 0

  @journal.push [node_object[0], k]
  new_refcount = Utils.encode_int(refcount-1)
  ref_put k, RLP.encode([new_refcount, node_object[1]])

  @death_row.push k if new_refcount == ZERO_ENCODED
end

#get(k) ⇒ Object



78
79
80
# File 'lib/ethereum/db/refcount_db.rb', line 78

def get(k)
  RLP.decode(ref_get(k))[1]
end

#get_refcount(k) ⇒ Object



71
72
73
74
75
76
# File 'lib/ethereum/db/refcount_db.rb', line 71

def get_refcount(k)
  o = Utils.decode_int RLP.decode(ref_get(k))[0]
  o >= DEATH_ROW_OFFSET ? 0 : o
rescue
  0
end

#has_key?(k) ⇒ Boolean Also known as: include?

Returns:

  • (Boolean)


158
159
160
# File 'lib/ethereum/db/refcount_db.rb', line 158

def has_key?(k)
  @db.has_key? ref_key(k)
end

#inc_refcount(k, v) ⇒ Object Also known as: put

Increase the reference count associated with a key.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/ethereum/db/refcount_db.rb', line 27

def inc_refcount(k, v)
  node_object = RLP.decode ref_get(k)
  refcount = Utils.decode_int node_object[0]

  @journal.push [node_object[0], k]
  refcount = 0 if refcount >= DEATH_ROW_OFFSET

  new_refcount = Utils.encode_int(refcount+1)
  ref_put k, RLP.encode([new_refcount, v])

  if logger.trace?
    logger.trace "increasing #{Utils.encode_hex(k)}=#{v} to #{refcount+1}"
  end
rescue
  ref_put k, RLP.encode([ONE_ENCODED, v])
  @journal.push [ZERO_ENCODED, k]

  if logger.trace?
    logger.trace "increasing #{Utils.encode_hex(k)}=#{v} to 1"
  end
end

#put_temporarily(k, v) ⇒ Object



163
164
165
166
# File 'lib/ethereum/db/refcount_db.rb', line 163

def put_temporarily(k, v)
  inc_refcount k, v
  dec_refcount k
end

#ref_get(k) ⇒ Object



172
173
174
175
176
177
178
# File 'lib/ethereum/db/refcount_db.rb', line 172

def ref_get(k)
  if has_key?(k)
    @db.get ref_key(k)
  else
    raise KeyError, k.inspect
  end
end

#ref_key(k) ⇒ Object



184
185
186
# File 'lib/ethereum/db/refcount_db.rb', line 184

def ref_key(k)
  "r:#{k}"
end

#ref_put(k, v) ⇒ Object



180
181
182
# File 'lib/ethereum/db/refcount_db.rb', line 180

def ref_put(k, v)
  @db.put ref_key(k), v
end

#revert_refcount_changes(epoch) ⇒ Object

Revert changes made during an epoch



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/ethereum/db/refcount_db.rb', line 142

def revert_refcount_changes(epoch)
  timeout_epoch = epoch + ttl

  @db.delete("deathrow:#{timeout_epoch}") rescue nil

  begin
    journal = RLP.decode @db.get("journal:#{epoch}")
    journal.reverse.each do |(new_refcount, key)|
      node_object = RLP.decode ref_get(key)
      ref_put key, RLP.encode([new_refcount, node_object[1]])
    end
  rescue
    # do nothing
  end
end