Commit c4a2b594b17feacfe4dcc791a9ea0178de6739d9

Authored by Marius Hanne
1 parent d12da20979

synchronize db access to sqlite via monitor

to avoid database busy exceptions

Showing 1 changed file with 80 additions and 65 deletions Side-by-side Diff

lib/bitcoin/storage/sequel.rb
... ... @@ -25,6 +25,13 @@
25 25 @config = config
26 26 connect
27 27 super config
  28 + if @config[:db] =~ /^sqlite/
  29 + require 'monitor'
  30 + @lock = Monitor.new
  31 + else
  32 + def synchronize; yield; end
  33 + @lock = self
  34 + end
28 35 end
29 36  
30 37 def connect
31 38  
32 39  
33 40  
34 41  
... ... @@ -93,39 +100,41 @@
93 100  
94 101 def store_block(blk)
95 102 @log.debug { "Storing block #{blk.hash} (#{blk.to_payload.bytesize} bytes)" }
96   - @db.transaction do
97   - existing = @db[:blk][:hash => htb(blk.hash).to_sequel_blob]
98   - if existing
99   - org_block(existing) if existing[:chain] != MAIN
100   - return
101   - end
  103 + @lock.synchronize do
  104 + @db.transaction do
  105 + existing = @db[:blk][:hash => htb(blk.hash).to_sequel_blob]
  106 + if existing
  107 + org_block(existing) if existing[:chain] != MAIN
  108 + return
  109 + end
102 110  
103   - block_id = @db[:blk].insert({
104   - :hash => htb(blk.hash).to_sequel_blob,
105   - :depth => -1,
106   - :chain => 2,
107   - :version => blk.ver,
108   - :prev_hash => blk.prev_block.reverse.to_sequel_blob,
109   - :mrkl_root => blk.mrkl_root.reverse.to_sequel_blob,
110   - :time => blk.time,
111   - :bits => blk.bits,
112   - :nonce => blk.nonce,
113   - :blk_size => blk.to_payload.bytesize,
114   - })
115   - blk.tx.each_with_index do |tx, idx|
116   - tx_id = store_tx(tx)
117   - raise "Error saving tx #{tx.hash} in block #{blk.hash}" unless tx_id
118   - @db[:blk_tx].insert({
119   - :blk_id => block_id,
120   - :tx_id => tx_id,
121   - :idx => idx,
  111 + block_id = @db[:blk].insert({
  112 + :hash => htb(blk.hash).to_sequel_blob,
  113 + :depth => -1,
  114 + :chain => 2,
  115 + :version => blk.ver,
  116 + :prev_hash => blk.prev_block.reverse.to_sequel_blob,
  117 + :mrkl_root => blk.mrkl_root.reverse.to_sequel_blob,
  118 + :time => blk.time,
  119 + :bits => blk.bits,
  120 + :nonce => blk.nonce,
  121 + :blk_size => blk.to_payload.bytesize,
122 122 })
123   - end
  123 + blk.tx.each_with_index do |tx, idx|
  124 + tx_id = store_tx(tx)
  125 + raise "Error saving tx #{tx.hash} in block #{blk.hash}" unless tx_id
  126 + @db[:blk_tx].insert({
  127 + :blk_id => block_id,
  128 + :tx_id => tx_id,
  129 + :idx => idx,
  130 + })
  131 + end
124 132  
125   - depth, chain = org_block(@db[:blk][:id => block_id])
  133 + depth, chain = org_block(@db[:blk][:id => block_id])
126 134  
127 135  
128   - return depth, chain
  136 + return depth, chain
  137 + end
129 138 end
130 139 rescue
131 140 @log.warn { "Error storing block: #{$!}" }
132 141  
133 142  
134 143  
135 144  
... ... @@ -134,55 +143,61 @@
134 143  
135 144 def store_tx(tx)
136 145 @log.debug { "Storing tx #{tx.hash} (#{tx.to_payload.bytesize} bytes)" }
137   - @db.transaction do
138   - transaction = @db[:tx][:hash => htb(tx.hash).to_sequel_blob]
139   - return transaction[:id] if transaction
140   - tx_id = @db[:tx].insert({
141   - :hash => htb(tx.hash).to_sequel_blob,
142   - :version => tx.ver,
143   - :lock_time => tx.lock_time,
144   - :coinbase => tx.in.size==1 && tx.in[0].coinbase?,
145   - :tx_size => tx.payload.bytesize,
146   - })
147   - tx.in.each_with_index {|i, idx| store_txin(tx_id, i, idx)}
148   - tx.out.each_with_index {|o, idx| store_txout(tx_id, o, idx)}
149   - tx_id
  146 + @lock.synchronize do
  147 + @db.transaction do
  148 + transaction = @db[:tx][:hash => htb(tx.hash).to_sequel_blob]
  149 + return transaction[:id] if transaction
  150 + tx_id = @db[:tx].insert({
  151 + :hash => htb(tx.hash).to_sequel_blob,
  152 + :version => tx.ver,
  153 + :lock_time => tx.lock_time,
  154 + :coinbase => tx.in.size==1 && tx.in[0].coinbase?,
  155 + :tx_size => tx.payload.bytesize,
  156 + })
  157 + tx.in.each_with_index {|i, idx| store_txin(tx_id, i, idx)}
  158 + tx.out.each_with_index {|o, idx| store_txout(tx_id, o, idx)}
  159 + tx_id
  160 + end
150 161 end
151 162 rescue
152 163 @log.warn { "Error storing tx: #{$!}" }
153 164 end
154 165  
155 166 def store_txin(tx_id, txin, idx)
156   - @db.transaction do
157   - @db[:txin].insert({
158   - :tx_id => tx_id,
159   - :tx_idx => idx,
160   - :script_sig => txin.script_sig.to_sequel_blob,
161   - :prev_out => txin.prev_out.to_sequel_blob,
162   - :prev_out_index => txin.prev_out_index,
163   - :sequence => txin.sequence.unpack("I")[0],
164   - })
  167 + @lock.synchronize do
  168 + @db.transaction do
  169 + @db[:txin].insert({
  170 + :tx_id => tx_id,
  171 + :tx_idx => idx,
  172 + :script_sig => txin.script_sig.to_sequel_blob,
  173 + :prev_out => txin.prev_out.to_sequel_blob,
  174 + :prev_out_index => txin.prev_out_index,
  175 + :sequence => txin.sequence.unpack("I")[0],
  176 + })
  177 + end
165 178 end
166 179 end
167 180  
168 181 def store_txout(tx_id, txout, idx)
169   - @db.transaction do
170   - script = Bitcoin::Script.new(txout.pk_script)
171   - txout_id = @db[:txout].insert({
172   - :tx_id => tx_id,
173   - :tx_idx => idx,
174   - :pk_script => txout.pk_script.to_sequel_blob,
175   - :value => txout.value,
176   - :type => SCRIPT_TYPES.index(script.type)
177   - })
178   - if script.is_hash160? || script.is_pubkey?
179   - store_addr(txout_id, script.get_hash160)
180   - elsif script.is_multisig?
181   - script.get_multisig_pubkeys.map do |pubkey|
182   - store_addr(txout_id, Bitcoin.hash160(pubkey.unpack("H*")[0]))
  182 + @lock.synchronize do
  183 + @db.transaction do
  184 + script = Bitcoin::Script.new(txout.pk_script)
  185 + txout_id = @db[:txout].insert({
  186 + :tx_id => tx_id,
  187 + :tx_idx => idx,
  188 + :pk_script => txout.pk_script.to_sequel_blob,
  189 + :value => txout.value,
  190 + :type => SCRIPT_TYPES.index(script.type)
  191 + })
  192 + if script.is_hash160? || script.is_pubkey?
  193 + store_addr(txout_id, script.get_hash160)
  194 + elsif script.is_multisig?
  195 + script.get_multisig_pubkeys.map do |pubkey|
  196 + store_addr(txout_id, Bitcoin.hash160(pubkey.unpack("H*")[0]))
  197 + end
183 198 end
  199 + txout_id
184 200 end
185   - txout_id
186 201 end
187 202 end
188 203