Class: Ethereum::DB::RefcountDB

Inherits:
BaseDB show all
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

Attributes inherited from BaseDB

#db

Instance Method Summary collapse

Constructor Details

#initialize(db) ⇒ RefcountDB

Returns a new instance of RefcountDB.



12
13
14
15
16
17
18
# File 'lib/ethereum/db/refcount_db.rb', line 12

def initialize(db)
  @db = db
  @journal = []
  @death_row = []
  @kv = @db.respond_to?(:kv) ? @db.kv : nil
  @ttl = 500
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.



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/ethereum/db/refcount_db.rb', line 82

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



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

def commit
  @db.commit
end

#commit_refcount_changes(epoch) ⇒ Object

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



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

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.extend @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:



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/ethereum/db/refcount_db.rb', line 49

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

  if Logger.trace?(logger.name)
    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(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



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

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

#get_refcount(k) ⇒ Object



67
68
69
70
71
72
# File 'lib/ethereum/db/refcount_db.rb', line 67

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)


154
155
156
# File 'lib/ethereum/db/refcount_db.rb', line 154

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.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/ethereum/db/refcount_db.rb', line 23

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.name)
    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.name)
    logger.trace "increasing #{Utils.encode_hex(k)}=#{v} to 1"
  end
end

#put_temporarily(k, v) ⇒ Object



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

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

#ref_get(k) ⇒ Object



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

def ref_get(k)
  @db.get ref_key(k)
end

#ref_key(k) ⇒ Object



176
177
178
# File 'lib/ethereum/db/refcount_db.rb', line 176

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

#ref_put(k, v) ⇒ Object



172
173
174
# File 'lib/ethereum/db/refcount_db.rb', line 172

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

#revert_refcount_changes(epoch) ⇒ Object

Revert changes made during an epoch



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/ethereum/db/refcount_db.rb', line 138

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