Class: Xampl::TokyoCabinetPersister

Inherits:
AbstractCachingPersister show all
Includes:
TokyoCabinet
Defined in:
lib/xamplr/persisters/tokyo-cabinet.rb

Overview

require ‘ruby-prof’

Instance Attribute Summary

Attributes inherited from Persister

#automatic, #block_changes, #cache_hits, #format, #last_write_count, #name, #read_count, #rolled_back, #syncing, #total_cache_hits, #total_read_count, #total_rollback_count, #total_sync_count, #total_write_count, #write_count

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from AbstractCachingPersister

#cache, #clear_cache, #dump, #fresh_cache, #read, #read_from_cache, #rollback_cleanup, #sync_done, #uncache, #write_to_cache

Methods inherited from Persister

#busy, #cache, #clear_cache, #count_changed, #find_known, #has_changed, #has_not_changed, #introduce, #is_busy, #lazy_load, #lookup, #print_stats, #put_changed, #read, #realise, replace, #represent, #rollback, #rollback_cleanup, #sync, #sync_done, #uncache, #version

Constructor Details

#initialize(name = nil, format = nil, root = File.join(".", "repo")) ⇒ TokyoCabinetPersister

Returns a new instance of TokyoCabinetPersister.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 40

def initialize(name=nil, format=nil, root=File.join(".", "repo"))
  super(root, name, format)

  FileUtils.mkdir_p(@root_dir) unless File.exist?(@root_dir)
  @filename = "#{@root_dir}/repo.tct"

  open_tc_db()

  #      note_errors("TC[[#{ @filename }]]:: optimisation error: %s\n") do
  #        @tc_db.optimize(-1, -1, -1, TDB::TDEFLATE)
  #      end
  #      note_errors("TC[[#{ @filename }]]:: close error: %s\n") do
  #        @tc_db.close
  #      end
end

Class Method Details

.add_lexical_indexs(indexes) ⇒ Object



32
33
34
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 32

def TokyoCabinetPersister.add_lexical_indexs(indexes)
  $lexical_indexes.merge(indexes)
end

.add_numeric_indexs(indexes) ⇒ Object



36
37
38
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 36

def TokyoCabinetPersister.add_numeric_indexs(indexes)
  $numeric_indexes.merge(indexes)
end

.kindObject



109
110
111
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 109

def TokyoCabinetPersister.kind
  :tokyo_cabinet
end

Instance Method Details

#closeObject



98
99
100
101
102
103
104
105
106
107
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 98

def close
  if @tc_db then
    self.sync
    note_errors("TC[[#{ @filename }]]:: close error: %s\n") do
      @tc_db.close
    end
    @tc_db = nil
#        puts "#{File.basename(__FILE__)}:#{__LINE__} close tc db: #{ @filename }"
  end
end

#do_sync_writeObject



291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 291

def do_sync_write
#      RubyProf.start
  #
  #      do_sync_write_work
  #
  #      result = RubyProf.stop
  #      printer = RubyProf::FlatPrinter.new(result)
  #      printer.print(STDOUT, 0)
  #      puts "#{File.basename(__FILE__)}:#{__LINE__} stop this profiler"
  #    end
  #
  #    def do_sync_write_work
  open_tc_db
  @time_stamp = Time.now.to_f.to_s

#            puts "DO SYNC WRITE: #{ @changed.size } to be written (#{ @filename })"
  #      note_errors("TC:: open error: %s\n") do
  #        @tc_db.open(@filename, TDB::OWRITER | TDB::OCREAT | TDB::OLCKNB ) #TDB::OTSYNC slows it down by almost 50 times
  #      end

  begin
    note_errors("TC[[#{ @filename }]]:: tranbegin error: %s\n") do
      @tc_db.tranbegin
    end

    @changed.each do |xampl, ignore|
      write(xampl)
    end
  rescue => e
    msg = "no TC.abort attempted"
    msg = note_errors("TC[[#{ @filename }]]:: trancommit error: %s\n") do
      @tc_db.tranabort
    end
    puts "------------------------------------------------------------------------"
    puts "TokyoCabinetPersister Error:: #{ msg }/#{ e }"
    puts e.backtrace.join("\n")
    puts "------------------------------------------------------------------------"
    raise "TokyoCabinetPersister Error:: #{ msg }/#{ e }"
  else
    note_errors("TC[[#{ @filename }]]:: trancommit error: %s\n") do
      @tc_db.trancommit
    end
  ensure
    #        note_errors("TC[[#{ @filename }]]:: close error: %s\n") do
    #          @tc_db.close()
    #        end
  end
#      puts "               num records: #{ @tc_db.rnum() }"
  #      puts "#{ __FILE__ }:#{ __LINE__ } keys..."
  #      @tc_db.keys.each do | key |
  #        meta = @tc_db[key]
  #        meta['xampl'] = (meta['xampl'] || "no rep")[0..25]
  #        puts "         key: [#{ key }] -- #{ meta.inspect }"
  #      end
end

#find_mentions_of(xampl) ⇒ Object



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 257

def find_mentions_of(xampl)
  open_tc_db

  place = File.join(xampl.class.name.split("::"), xampl.get_the_index)

  query = TableQuery.new(@tc_db)
  query.add_condition('xampl-to', :equals, place)
  result_keys = query.search

  class_cache = {}
  results = result_keys.collect do | key |
    result = @tc_db[ key ]
    next unless result

    mentioner = result['xampl-from']
    class_name = result['mentioned_class']
    result_class = class_cache[class_name]
    unless result_class then
      class_name.split("::").each do | chunk |
        if result_class then
          result_class = result_class.const_get( chunk )
        else
          result_class = Kernel.const_get( chunk )
        end
      end

      class_cache[class_name] = result_class
    end

    self.lookup(result_class, result['pid'])
  end
  return results.uniq
end

#find_meta(hint = false) {|query| ... } ⇒ Object

Yields:



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 234

def find_meta(hint=false)
  open_tc_db
  query = TableQuery.new(@tc_db)

  yield query

  result_keys = nil
  the_hint = nil
  if hint then
    result_keys, the_hint = query.search(true)
  else
    result_keys = query.search
  end

  results = result_keys.collect { | key | @tc_db[ key ] }

  if hint then
    return results, the_hint
  else
    return results
  end
end

#find_pids(hint = false) {|query| ... } ⇒ Object

Yields:



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 208

def find_pids(hint=false)
  open_tc_db
  query = TableQuery.new(@tc_db)

  yield query

  result_keys = nil
  the_hint = nil
  if hint then
    result_keys, the_hint = query.search(true)
  else
    result_keys = query.search
  end

  results = result_keys.collect do |key|
    meta = @tc_db[ key ]
    meta['xampl-place'] || meta['place']
  end

  if hint then
    return results.uniq, the_hint
  else
    return results.uniq
  end
end

#find_xampl(hint = false) {|query| ... } ⇒ Object

Yields:



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 164

def find_xampl(hint=false)
  open_tc_db
  query = TableQuery.new(@tc_db)

  yield query

  class_cache = {}

  result_keys = nil
  the_hint = nil
  if hint then
    result_keys, the_hint = query.search(true)
  else
    result_keys = query.search
  end

  results = result_keys.collect do | key |
    result = @tc_db[ key ]
    next unless result

    class_name = result['class']
    result_class = class_cache[class_name]
    unless result_class then
      class_name.split("::").each do | chunk |
        if result_class then
          result_class = result_class.const_get( chunk )
        else
          result_class = Kernel.const_get( chunk )
        end
      end

      class_cache[class_name] = result_class
    end

    self.lookup(result_class, result['pid'])
  end

  if hint then
    return results.uniq, the_hint
  else
    return results.uniq
  end
end

#kindObject



113
114
115
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 113

def kind
  TokyoCabinetPersister.kind
end

#note_errors(msg = "TokyoCabinet Error:: %s\n") ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 13

def note_errors(msg="TokyoCabinet Error:: %s\n")
  result = yield

  rmsg = nil
  unless result then
    rmsg = sprintf(msg, @tc_db.errmsg(@tc_db.ecode))
    STDERR.printf(rmsg)
    STDERR.puts "---------"
    caller(0).each do |trace|
      STDERR.puts(trace)
    end
    STDERR.puts "---------"
  end
  return rmsg
end

#open_tc_dbObject



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 56

def open_tc_db
  return if @tc_db
#      puts "#{File.basename(__FILE__)}:#{__LINE__} open tc db: #{ @filename }"
  #puts "#{File.basename(__FILE__)}:#{__LINE__} callers..."
  #caller(0).each { | trace | puts "   #{trace}"}
  @tc_db = TDB.new
  note_errors("TC[[#{ @filename }]]:: tuning error: %s\n") do
    @tc_db.tune(-1, -1, -1, TDB::TDEFLATE)
  end

  note_errors("TC[[#{ @filename }]]:: open [#{ @filename }] error: %s\n") do
    @tc_db.open(@filename, TDB::OWRITER | TDB::OCREAT | TDB::OLCKNB ) #TDB::OTSYNC slows it down by almost 50 times
  end

  # Don't care if there are errors (in fact, if the index exists a failure is the expected thing)

  $lexical_indexes.each do | index_name |
    r = @tc_db.setindex(index_name, TDB::ITLEXICAL | TDB::ITKEEP)
  end
  $numeric_indexes.each do | index_name |
    @tc_db.setindex(index_name, TDB::ITDECIMAL | TDB::ITKEEP)
  end
end

#optimise(opts) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 80

def optimise(opts)
  return unless @tc_db

  if opts[:indexes_only] then
    # Don't care if there are errors (in fact, if the index exists a failure is the expected thing)
    $lexical_indexes.each do | index_name |
      @tc_db.setindex(index_name, 9998)
    end
    $numeric_indexes.each do | index_name |
      @tc_db.setindex(index_name, 9998)
    end
  else
    note_errors("TC[[#{ @filename }]]:: optimisation error: %s\n") do
      @tc_db.optimize(-1, -1, -1, 0xff)
    end
  end
end

#query(hint = false) {|query| ... } ⇒ Object

Yields:



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 121

def query(hint=false)
  open_tc_db
  query = TableQuery.new(@tc_db)

  yield query

  result_keys = nil
  the_hint = nil
  if hint then
    result_keys, the_hint = query.search(true)
  else
    result_keys = query.search
  end
  results = result_keys.collect { | key | @tc_db[ key ] }

  class_cache = {}
  results.each do | result |
    next unless result

    class_name = result['class']
    result_class = class_cache[class_name]
    unless result_class then
      class_name.split("::").each do | chunk |
        if result_class then
          result_class = result_class.const_get( chunk )
        else
          result_class = Kernel.const_get( chunk )
        end
      end

      class_cache[class_name] = result_class
    end

    result['xampl'] = self.lookup(result_class, result['pid'])
  end

  if hint then
    return results.uniq, the_hint
  else
    return results.uniq
  end
end

#query_implementedObject



117
118
119
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 117

def query_implemented
  true
end

#read_representation(klass, pid) ⇒ Object



425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 425

def read_representation(klass, pid)
  #TODO -- is this being called too often, e.g. by new_xxx???
  #      puts "#{File.basename(__FILE__)}:#{__LINE__} READ #{ klass }/#{ pid }"
  #      caller(0).each { | trace | puts "  #{trace}"}

  open_tc_db
  place = File.join(klass.name.split("::"), pid)
  representation = nil

  meta = @tc_db[place]
  representation = meta['xampl'] if meta

  #      puts "#{File.basename(__FILE__)}:#{__LINE__} TC: #{ klass }/#{ pid }" if representation
  $TC_COUNT += 1 if representation

  # puts "read: #{ place }, size: #{ representation.size }"
  # puts representation[0..100]

  unless representation then
    # try the filesystem if it is not in the TC repository
    place = File.join(@root_dir, klass.name.split("::"), pid)
    representation = File.read(place) if File.exist?(place)
    $FS_COUNT += 1 if representation
#        puts "#{File.basename(__FILE__)}:#{__LINE__} FS: #{ klass }/#{ pid } (FS: #{ $FS_COUNT}, TC: #{ $TC_COUNT }, NF: #{ $NF_COUNT }" if representation
  end
#      puts "#{File.basename(__FILE__)}:#{__LINE__} ??: #{ klass }/#{ pid }" unless representation
  $NF_COUNT += 1

  return representation
end

#write(xampl) ⇒ Object

Raises:



347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 347

def write(xampl)
  raise XamplException.new(:no_index_so_no_persist) unless xampl.get_the_index

  place = File.join(xampl.class.name.split("::"), xampl.get_the_index)
  mentions = Set.new
  data = represent(xampl, mentions)

  #get rid of any supplimentary indexes associated with this xampl object
  query = TableQuery.new(@tc_db)
  query.add_condition('xampl-from', :equals, place)
  note_errors("TC[[#{ @filename }]]:: failed to remove from mentions, error: %s\n") do
    query.searchout
  end

  query = TableQuery.new(@tc_db)
  query.add_condition('xampl-place', :equals, place)
  note_errors("TC[[#{ @filename }]]:: failed to remove from mentions, error: %s\n") do
    query.searchout
  end

  mentions.each do | mention |
    mention_place = File.join(mention.class.name.split("::"), mention.get_the_index)
    #TODO -- will repeadedly changing a persisted xampl object fragment the TC db?

    pk = @tc_db.genuid
    mention_hash = {
            'xampl-from' => place,
            'mentioned_class' => xampl.class.name,
            'pid' => xampl.get_the_index,
            'xampl-to' => mention_place
    }

    note_errors("TC[[#{ @filename }]]:: write error: %s\n") do
      @tc_db.put(pk, mention_hash)
    end
  end

  xampl_hash = {
          'class' => xampl.class.name,
          'pid' => xampl.get_the_index,
          'time-stamp' => @time_stamp,
          'xampl' => data
  }

  primary_description, secondary_descriptions = xampl.describe_yourself
  if primary_description then
    xampl_hash = primary_description.merge(xampl_hash)
  end

  note_errors("TC[[#{ @filename }]]:: write error: %s\n") do
    @tc_db.put(place, xampl_hash)
  end

  if secondary_descriptions then
    xampl_hash = {
            'class' => xampl.class.name,
            'pid' => xampl.get_the_index,
            'xampl-place' => place
    }
    secondary_descriptions.each do | secondary_description |
      description = secondary_description.merge(xampl_hash)

      note_errors("TC[[#{ @filename }]]:: write error: %s\n") do
        pk = @tc_db.genuid
        @tc_db.put(pk, description)
      end
    end
  end

  @write_count = @write_count + 1
  xampl.changes_accepted
  return true
end