class Ethereum::DB::RefcountDB

Constants

DEATH_ROW_OFFSET
ONE_ENCODED
ZERO_ENCODED

Attributes

ttl[RW]

Public Class Methods

new(db) click to toggle source
# File lib/ethereum/db/refcount_db.rb, line 14
def initialize(db)
  @db = db
  @journal = []
  @death_row = []
  @kv = @db.respond_to?(:kv) ? @db.kv : nil

  self.ttl = 500
end

Public Instance Methods

cleanup(epoch) click to toggle source

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

# File lib/ethereum/db/refcount_db.rb, line 85
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
commit() click to toggle source
# File lib/ethereum/db/refcount_db.rb, line 167
def commit
  @db.commit
end
commit_refcount_changes(epoch) click to toggle source

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

# File lib/ethereum/db/refcount_db.rb, line 112
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) click to toggle source

Decrease the reference count associated with a key.

# File lib/ethereum/db/refcount_db.rb, line 52
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
Also aliased as: delete
delete(k)
Alias for: dec_refcount
get(k) click to toggle source
# File lib/ethereum/db/refcount_db.rb, line 77
def get(k)
  RLP.decode(ref_get(k))[1]
end
get_refcount(k) click to toggle source
# File lib/ethereum/db/refcount_db.rb, line 70
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) click to toggle source
# File lib/ethereum/db/refcount_db.rb, line 157
def has_key?(k)
  @db.has_key? ref_key(k)
end
Also aliased as: include?
inc_refcount(k, v) click to toggle source

Increase the reference count associated with a key.

# File lib/ethereum/db/refcount_db.rb, line 26
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
Also aliased as: put
include?(k)
Alias for: has_key?
put(k, v)
Alias for: inc_refcount
put_temporarily(k, v) click to toggle source
# File lib/ethereum/db/refcount_db.rb, line 162
def put_temporarily(k, v)
  inc_refcount k, v
  dec_refcount k
end
ref_get(k) click to toggle source
# File lib/ethereum/db/refcount_db.rb, line 171
def ref_get(k)
  if has_key?(k)
    @db.get ref_key(k)
  else
    raise KeyError, k.inspect
  end
end
ref_key(k) click to toggle source
# File lib/ethereum/db/refcount_db.rb, line 183
def ref_key(k)
  "r:#{k}"
end
ref_put(k, v) click to toggle source
# File lib/ethereum/db/refcount_db.rb, line 179
def ref_put(k, v)
  @db.put ref_key(k), v
end
revert_refcount_changes(epoch) click to toggle source

Revert changes made during an epoch

# File lib/ethereum/db/refcount_db.rb, line 141
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

Private Instance Methods

logger() click to toggle source
# File lib/ethereum/db/refcount_db.rb, line 189
def logger
  @logger ||= Logger.new 'eth.db.refcount'
end