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, #expunged, #format, #last_write_count, #name, #read_count, #rolled_back, #slow_sync, #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, #shutdown, #sync, #sync_done, #uncache

Constructor Details

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

Returns a new instance of TokyoCabinetPersister.



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 58

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

  @files_dir = "#{ @root_dir }/files"
#      FileUtils.mkdir_p(@root_dir) unless File.exist?(@root_dir)
  FileUtils.mkdir_p(@files_dir) unless File.exist?(@files_dir)
  @filename = "#{@root_dir}/repo.tct"
  @tc_db = nil
#      puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] file: #{ @filename }, db: #{ @tc_db.class.name }"

  setup_db

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

  begin
    note_errors("TC[[#{ @filename }]]:: close error in initialize: %s\n") do
      @tc_db.close
    end
    @tc_db = nil
  rescue => e
    #TODO -- why do this???
    puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] OH CRAP!!! #{ e }"
    puts e.backtrace
  end
end

Class Method Details

.add_lexical_indexs(indexes) ⇒ Object



50
51
52
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 50

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

.add_numeric_indexs(indexes) ⇒ Object



54
55
56
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 54

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

.kindObject



175
176
177
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 175

def TokyoCabinetPersister.kind
  :tokyo_cabinet
end

Instance Method Details

#backup(base_path) ⇒ Object



678
679
680
681
682
683
684
685
686
687
688
689
690
691
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 678

def backup(base_path)

  dir_path = "#{ base_path }/#{ @root_dir }/"
  path = "#{ dir_path }/repo.tct"

  FileUtils.mkdir_p(dir_path)

  note_errors("TC[[#{ dir_path }]]:: backup error: %s\n") do
    @tc_db.copy(path)
  end
  return true
rescue => e
  return false
end

#closeObject



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 152

def close
#      puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] CLOSE #{ @filename }"
#      callers = caller(0)
#      puts "   0 #{ callers[0] }"
#      puts "   1 #{ callers[1] }"
#      puts "   2 #{ callers[2] }"

  if @tc_db then
    begin
#          puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] NO SELF SYNC?? [#{ @currently_syncing }] --> db: #{ @tc_db.class.name }"
      self.sync unless @currently_syncing
    rescue => e
      #TODO -- why do this
      puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] OH CRAP!!! #{ e }"
    ensure
      note_errors("TC[[#{ @filename }]]:: close error: %s\n") do
        @tc_db.close
      end
      @tc_db = nil
    end
  end
end

#do_sync_writeObject



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
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 381

def do_sync_write
  begin
#@start = Time.now
#@last = Time.now
#puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] time: #{ Time.now - @start }/#{ Time.now - @last }"; @last = Time.now
#        puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] **************************"
#        callers = caller(0)
#        puts "   0 #{ callers[0] }"
#        puts "   1 #{ callers[1] }"
#        puts "   2 #{ callers[2] }"

#        setup_db
    @time_stamp = Time.now.to_f.to_s

    note_errors("TC[[#{ @filename }]]:: tranbegin error: %s\n") do
      @tc_db.tranbegin
    end
#puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] time: #{ Time.now - @start }/#{ Time.now - @last }"; @last = Time.now

    @changed.each do |xampl, ignore|
      write(xampl)
#puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] time: #{ Time.now - @start }/#{ Time.now - @last }"; @last = Time.now
    end
  rescue => e
    msg = "no TC.abort attempted"
    msg = note_errors("TC[[#{ @filename }]]:: tranabort 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 }"
    raise RuntimeError, "TokyoCabinetPersister Error:: #{ msg }/#{ e }", e.backtrace
  else
#        puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] COMMIT"
#puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] time: #{ Time.now - @start }/#{ Time.now - @last }"; @last = Time.now
    note_errors("TC[[#{ @filename }]]:: trancommit error: %s\n") do
      @tc_db.trancommit
    end
#puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] time: #{ Time.now - @start }/#{ Time.now - @last }"; @last = Time.now
  ensure
#      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

#        close
#puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] time: #{ Time.now - @start }/#{ Time.now - @last }"; @last = Time.now
  end
end

#done_sync_writeObject



369
370
371
372
373
374
375
376
377
378
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 369

def done_sync_write
  begin
    note_errors("TC[[#{ @filename }]]:: sync error in done_sync_write: %s\n") do
      @tc_db.sync
    end
#        close
  ensure
    @currently_syncing = false
  end
end

#expunge(xampl) ⇒ Object



474
475
476
477
478
479
480
481
482
483
484
485
486
487
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 474

def expunge(xampl)
  #NOTE -- this *must* be in a transaction
  #NOTE -- the expunge operation is in two steps and is completed in write

  mentions = Xampl.find_mentions_of(xampl)
  mentions.each do | has_a_xampl |
#        xampl.remove_from(has_a_xampl)
    remove_all_mention(has_a_xampl, xampl)
  end
  xampl.changed
  self.expunged << xampl

  false
end

#find_mentions_of(xampl) ⇒ Object



324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 324

def find_mentions_of(xampl)
  setup_db

  place = File.join(xampl.class.persistence_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:



301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 301

def find_meta(hint=false)
  setup_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:



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 275

def find_pids(hint=false)
  setup_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:



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 230

def find_xampl(hint=false)
  setup_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

#how_indexed(xampl) ⇒ Object

Raises:



436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 436

def how_indexed(xampl)
  raise XamplException.new(:no_index_so_no_persist) unless xampl.get_the_index
  place = File.join(xampl.class.persistence_class.name.split("::"), xampl.get_the_index)

  setup_db

  result_keys = Set.new

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

  primary = @tc_db[ place ]
  if primary then
    primary.delete('xampl')
  end

  results = primary ? [ primary ] : []
  result_keys.each do | key |
    result = @tc_db[ key ]
    next unless result

    result.delete('xampl')

    results << result
  end

  results
end

#kindObject



179
180
181
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 179

def kind
  TokyoCabinetPersister.kind
end

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



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 17

def note_errors(msg="TokyoCabinet Error:: %s\n")
#      puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] START"
#      puts "--------------------------------------------------"
#      caller(0).each { |trace| puts " #{trace}" }
#      puts "--------------------------------------------------\n\n"
  exception = nil
  begin
    result = yield
  rescue => e
    exception = e
  end

  rmsg = nil
  unless result then
    if @tc_db then
      rmsg = sprintf(msg, @tc_db.errmsg(@tc_db.ecode))
    else
      rmsg = "either not actually a TokyoCabinet Error, or @tc_db was never opened"
    end
    STDERR.puts "NOTE: TokyoCabinet Error at #{ Time.now }!"
    STDERR.puts(rmsg)
    STDERR.puts "---------"
    STDERR.puts caller(0)
    STDERR.puts "---------"
  end
  raise exception if exception
  return rmsg
end

#open_tc_dbObject



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 94

def open_tc_db
#      if @tcdb then
#        puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] ALREADY OPEN #{ @filename }"
#      else
#        puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] OPEN #{ @filename }"
##        callers = caller(0)
##        puts "   0 #{ callers[0] }"
##        puts "   1 #{ callers[1] }"
##        puts "   2 #{ callers[2] }"
#        #puts "#{File.basename(__FILE__)}:#{__LINE__} callers..."
#        #caller(0).each { | trace | puts "   #{trace}"}
#      end

  return if @tc_db # if there is a tc_db then it is already open

  @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
    if Xampl.raw_persister_options[:otsync] then
      @tc_db.open(@filename, TDB::OWRITER | TDB::OCREAT | TDB::OLCKNB | TDB::OTSYNC ) #TDB::OTSYNC slows it down by almost 50 times
    else
      @tc_db.open(@filename, TDB::OWRITER | TDB::OCREAT | TDB::OLCKNB)
    end
  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

  optimise
end

#optimise(opts = {}) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 134

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:



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 187

def query(hint=false)
  setup_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



183
184
185
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 183

def query_implemented
  true
end

#read_representation(klass, pid) ⇒ Object



640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 640

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}"}

  representation = nil

  unless @tc_db then
#        puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] READ REP"
    setup_db
  end

  place = File.join(klass.persistence_class.name.split("::"), pid)

  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.persistence_class.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

  #TODO this is coming back as ASCII-8BIT rather than UTF-8!!!!!!!!!!!!!!!!!!
#      puts "#{ ::File.basename __FILE__ }:#{ __LINE__ } [#{__method__}] encoding #{ representation.encoding }"
  return representation
end

#remove_all_mention(root, xampl) ⇒ Object



467
468
469
470
471
472
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 467

def remove_all_mention(root, xampl)
  xampl.remove_from(root)
  root.children.each do | child |
    remove_all_mention(child, xampl)
  end
end

#setup_dbObject



87
88
89
90
91
92
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 87

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

#start_sync_writeObject



358
359
360
361
362
363
364
365
366
367
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 358

def start_sync_write
#      puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] #{ @filename }"
#      callers = caller(0)
#      puts "   0 #{ callers[0] }"
#      puts "   1 #{ callers[1] }"
#      puts "   2 #{ callers[2] }"

  @currently_syncing = true
  setup_db
end

#write(xampl) ⇒ Object

Raises:



489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
# File 'lib/xamplr/persisters/tokyo-cabinet.rb', line 489

def write(xampl)

#      STDOUT.puts "#{ File.basename __FILE__ }:#{ __LINE__ } [#{__method__}] write #{ xampl }"
  raise XamplException.new(:no_index_so_no_persist) unless xampl.get_the_index

  expunging = self.expunged.include?(xampl)
  self.expunged.delete(xampl) if expunging
#      if expunging
#        puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] EXPUNGING #{ xampl }/#{ expunging }"
#      end

  place_dir = xampl.class.persistence_class.name.split("::")
  place = File.join( place_dir, xampl.get_the_index)
  place_dir = File.join( @files_dir, place_dir )
  mentions = Set.new
  xampl_in_xml = represent(xampl, mentions)
  unless xampl_in_xml && 0 < xampl_in_xml.size then
    puts "Cannot persist #{ xampl } because representation is unobtainable"
    return
  end

  #get rid of any supplimentary indexes associated with this xampl object
  # TODO -- This can be slow
  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

  if expunging then
    file_place = "#{ @files_dir }/#{ place }"
    File.delete(file_place) if File.exists?(file_place)
    note_errors("TC[[#{ place }]]:: write error: %s\n") do
      @tc_db.out(place)
    end

    uncache(xampl)
  else
    if Xampl.raw_persister_options[:mentions] then
      # TODO -- This can be slow
      mentions.each do | mention |
        mention_place = File.join(mention.class.persistence_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
    end

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

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

    index_info = {}
    if primary_description && 0 < primary_description.size then
      index_info[:primary] = xampl_hash.dup
      index_info[:primary].delete('xampl')
    end
    index_info[:secondary] = []

    #TODO -- smarter regarding when to delete (e.g. mentions)
    if xampl.should_schedule_delete? and xampl.scheduled_for_deletion_at then
      secondary_descriptions = [] unless secondary_descriptions
      secondary_descriptions << { 'scheduled-delete-at' => xampl.scheduled_for_deletion_at }
    elsif xampl.scheduled_for_deletion_at then
      #TODO -- puts "#{ __FILE__ }:#{ __LINE__ } HOW TO DO THIS without violating xampl's change rules????? "
      #xampl.scheduled_for_deletion_at = nil
    end

    if secondary_descriptions then
      secondary_xampl_hash = {
              'class' => xampl.class.name,
              'pid' => xampl.get_the_index,
              'xampl-place' => place
      }

      secondary_descriptions.each do | secondary_description |
        description = secondary_description.merge(secondary_xampl_hash)
        index_info[:secondary] << secondary_description

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

    note_errors("TC[[#{ @filename }]]:: write error: %s\n") do
      begin
        if Xampl.raw_persister_options[:write_through] then
          FileUtils.mkdir_p(place_dir) unless File.exist?(place_dir)
          file_place = "#{ @files_dir }/#{ place }"
          File.open(file_place, "w") do |out|
#                out.write xampl_hash['xampl']
            out.write xampl_in_xml
            if :sync == Xampl.raw_persister_options[:write_through] then
              out.fsync
              if $is_darwin then
                out.fcntl(51, 0) # Attempt an F_FULLFSYNC fcntl to commit data to disk (darwin *ONLY*)
              end
            end
          end
          #NOTE I've changed to to an || from && (so it'll write if only either primary or secondary is defined)
          if index_info[:primary] || 0 < index_info[:secondary].size then
            file_place += ".idx"
            File.open(file_place, "w") do |out|
              out.write index_info.to_yaml
            end
          end
        end
      rescue => e
        puts "#{ File.basename __FILE__ }:#{ __LINE__ } [#{__method__}] write through failed #{ xampl }"
      end

      @tc_db.put(place, xampl_hash)
    end

    @write_count = @write_count + 1
    xampl.changes_accepted
  end

  return true
end