1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.mavibot.btree;
21
22
23 import java.io.Closeable;
24 import java.io.IOException;
25 import java.nio.ByteBuffer;
26 import java.nio.channels.FileChannel;
27 import java.util.concurrent.ConcurrentLinkedQueue;
28
29 import org.apache.commons.collections.map.LRUMap;
30 import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34
35
36
37
38
39
40
41
42
43 public class PersistedBTree<K, V> extends AbstractBTree<K, V> implements Closeable
44 {
45
46 protected static final Logger LOG = LoggerFactory.getLogger( PersistedBTree.class );
47
48 protected static final Logger LOG_PAGES = LoggerFactory.getLogger( "org.apache.directory.mavibot.LOG_PAGES" );
49
50
51 protected LRUMap cache;
52
53
54 public static final int DEFAULT_CACHE_SIZE = 1000;
55
56
57 protected int cacheSize = DEFAULT_CACHE_SIZE;
58
59
60 private static final int DEFAULT_VALUE_THRESHOLD_UP = 8;
61
62
63 private static final int DEFAULT_VALUE_THRESHOLD_LOW = 1;
64
65
66 static int valueThresholdUp = DEFAULT_VALUE_THRESHOLD_UP;
67 static int valueThresholdLow = DEFAULT_VALUE_THRESHOLD_LOW;
68
69
70 private long btreeInfoOffset;
71
72
73 private RecordManager recordManager;
74
75
76
77
78 PersistedBTree()
79 {
80 setType( BTreeTypeEnum.PERSISTED );
81 }
82
83
84
85
86
87
88
89
90 PersistedBTree( PersistedBTreeConfiguration<K, V> configuration )
91 {
92 super();
93 String name = configuration.getName();
94
95 if ( name == null )
96 {
97 throw new IllegalArgumentException( "BTree name cannot be null" );
98 }
99
100 setName( name );
101 setPageSize( configuration.getPageSize() );
102 setKeySerializer( configuration.getKeySerializer() );
103 setValueSerializer( configuration.getValueSerializer() );
104 setAllowDuplicates( configuration.isAllowDuplicates() );
105 setType( configuration.getBtreeType() );
106
107 readTimeOut = configuration.getReadTimeOut();
108 writeBufferSize = configuration.getWriteBufferSize();
109 cacheSize = configuration.getCacheSize();
110
111 if ( keySerializer.getComparator() == null )
112 {
113 throw new IllegalArgumentException( "Comparator should not be null" );
114 }
115
116
117
118 Page<K, V> rootPage = new PersistedLeaf<K, V>( this );
119
120
121 BTreeHeader<K, V> btreeHeader = new BTreeHeader<K, V>();
122 btreeHeader.setRootPage( rootPage );
123 btreeHeader.setBtree( this );
124
125 switch ( btreeType )
126 {
127 case BTREE_OF_BTREES :
128 case COPIED_PAGES_BTREE :
129
130 init( null );
131 currentBtreeHeader = btreeHeader;
132 break;
133
134 case PERSISTED_SUB :
135 init( ( PersistedBTree<K, V> ) configuration.getParentBTree() );
136 btreeRevisions.put( 0L, btreeHeader );
137 currentBtreeHeader = btreeHeader;
138 break;
139
140 default :
141
142 init( null );
143 btreeRevisions.put( 0L, btreeHeader );
144 currentBtreeHeader = btreeHeader;
145 break;
146 }
147 }
148
149
150
151
152
153
154
155 public void init( BTree<K, V> parentBTree )
156 {
157 if ( parentBTree == null )
158 {
159
160
161
162 readTransactions = new ConcurrentLinkedQueue<ReadTransaction<K, V>>();
163
164 if ( cacheSize < 1 )
165 {
166 cacheSize = 1;
167 }
168
169 cache = new LRUMap( cacheSize );
170 }
171 else
172 {
173 this.cache = ((PersistedBTree<K, V>)parentBTree).getCache();
174 this.readTransactions = ((PersistedBTree<K, V>)parentBTree).getReadTransactions();
175 }
176
177
178
179
180 }
181
182
183
184
185
186 LRUMap getCache()
187 {
188 return cache;
189 }
190
191
192
193
194
195 ConcurrentLinkedQueue<ReadTransaction<K, V>> getReadTransactions()
196 {
197 return readTransactions;
198 }
199
200
201
202
203
204 public void close() throws IOException
205 {
206
207
208
209
210
211 cache.clear();
212 }
213
214
215
216
217
218 long getBtreeOffset()
219 {
220 return getBTreeHeader( getName() ).getBTreeHeaderOffset();
221 }
222
223
224
225
226
227 void setBtreeHeaderOffset( long btreeHeaderOffset )
228 {
229 getBTreeHeader( getName() ).setBTreeHeaderOffset( btreeHeaderOffset );
230 }
231
232
233
234
235
236 long getRootPageOffset()
237 {
238 return getBTreeHeader( getName() ).getRootPageOffset();
239 }
240
241
242
243
244
245
246
247 RecordManager getRecordManager()
248 {
249 return recordManager;
250 }
251
252
253
254
255
256
257
258 void setRecordManager( RecordManager recordManager )
259 {
260
261 transactionManager = recordManager;
262 this.recordManager = recordManager;
263 }
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278 Tuple<K, V> delete( K key, V value, long revision ) throws IOException
279 {
280
281
282 if ( revision == -1L )
283 {
284 revision = currentRevision.get() + 1;
285 }
286
287 try
288 {
289
290
291 DeleteResult<K, V> result = processDelete( key, value, revision );
292
293
294 if ( result instanceof NotPresentResult )
295 {
296
297
298
299 return null;
300 }
301
302
303 AbstractDeleteResult<K, V> deleteResult = ( AbstractDeleteResult<K, V> ) result;
304
305 Tuple<K, V> tuple = deleteResult.getRemovedElement();
306
307
308
309
310
311 return tuple;
312 }
313 catch ( IOException ioe )
314 {
315
316 throw ioe;
317 }
318 }
319
320
321
322
323
324 private DeleteResult<K, V> processDelete( K key, V value, long revision ) throws IOException
325 {
326
327 BTreeHeader<K, V> btreeHeader = getBTreeHeader( getName() );
328
329
330
331 DeleteResult<K, V> result = btreeHeader.getRootPage().delete( key, value, revision);
332
333 if ( result instanceof NotPresentResult )
334 {
335
336 return result;
337 }
338
339
340 BTreeHeader<K, V> newBtreeHeader = btreeHeader.copy();
341
342
343
344 if ( ( btreeType == BTreeTypeEnum.BTREE_OF_BTREES ) || ( btreeType == BTreeTypeEnum.COPIED_PAGES_BTREE ) )
345 {
346 PageIO[] pageIos = recordManager.readPageIOs( btreeHeader.getBTreeHeaderOffset(), -1L );
347
348 for ( PageIO pageIo : pageIos )
349 {
350 recordManager.freedPages.add( pageIo );
351 }
352 }
353
354
355 AbstractDeleteResult<K, V> removeResult = ( AbstractDeleteResult<K, V> ) result;
356
357
358 Page<K, V> newRootPage = removeResult.getModifiedPage();
359
360
361
362
363 PageHolder<K, V> holder = writePage( newRootPage, revision );
364
365
366 newBtreeHeader.decrementNbElems();
367 newBtreeHeader.setRootPage( newRootPage );
368 newBtreeHeader.setRevision( revision );
369
370
371 long newBtreeHeaderOffset = recordManager.writeBtreeHeader( this, newBtreeHeader );
372
373
374
375 switch ( btreeType )
376 {
377 case PERSISTED :
378
379 recordManager.addInBtreeOfBtrees( getName(), revision, newBtreeHeaderOffset );
380
381 recordManager.addInCopiedPagesBtree( getName(), revision, result.getCopiedPages() );
382
383
384 storeRevision( newBtreeHeader, recordManager.isKeepRevisions() );
385
386 break;
387
388 case PERSISTED_SUB :
389
390 recordManager.addInCopiedPagesBtree( getName(), revision, result.getCopiedPages() );
391
392
393
394 currentRevision.set( revision );
395
396
397 break;
398
399 case BTREE_OF_BTREES :
400
401 recordManager.updateRecordManagerHeader( newBtreeHeaderOffset, -1L );
402
403
404 recordManager.freePages( this, revision, result.getCopiedPages() );
405
406
407 storeRevision( newBtreeHeader, recordManager.isKeepRevisions() );
408
409 break;
410
411 case COPIED_PAGES_BTREE :
412
413 recordManager.updateRecordManagerHeader( -1L, newBtreeHeaderOffset );
414
415
416 recordManager.freePages( this, revision, result.getCopiedPages() );
417
418
419 storeRevision( newBtreeHeader, recordManager.isKeepRevisions() );
420
421 break;
422
423 default:
424
425 break;
426 }
427
428
429 return result;
430 }
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446 InsertResult<K, V> insert( K key, V value, long revision ) throws IOException
447 {
448
449
450 if ( revision == -1L )
451 {
452 revision = currentRevision.get() + 1;
453 }
454
455 try
456 {
457
458
459
460 InsertResult<K, V> result = processInsert( key, value, revision );
461
462
463 return result;
464 }
465 catch ( IOException ioe )
466 {
467 throw ioe;
468 }
469 }
470
471
472 private BTreeHeader<K, V> getBTreeHeader( String name )
473 {
474 switch ( btreeType )
475 {
476 case PERSISTED_SUB :
477 return getBtreeHeader();
478
479 case BTREE_OF_BTREES :
480 return recordManager.getNewBTreeHeader( RecordManager.BTREE_OF_BTREES_NAME );
481
482 case COPIED_PAGES_BTREE :
483 return recordManager.getNewBTreeHeader( RecordManager.COPIED_PAGE_BTREE_NAME );
484
485 default :
486 return recordManager.getBTreeHeader( getName() );
487 }
488 }
489
490
491 private BTreeHeader<K, V> getNewBTreeHeader( String name )
492 {
493 if ( btreeType == BTreeTypeEnum.PERSISTED_SUB )
494 {
495 return getBtreeHeader();
496 }
497
498 BTreeHeader<K, V> btreeHeader = recordManager.getNewBTreeHeader( getName() );
499
500 return btreeHeader;
501 }
502
503
504
505
506
507 private InsertResult<K, V> processInsert( K key, V value, long revision ) throws IOException
508 {
509
510 BTreeHeader<K, V> btreeHeader = getBTreeHeader( getName() );
511 InsertResult<K, V> result = btreeHeader.getRootPage().insert( key, value, revision );
512
513 if ( result instanceof ExistsResult )
514 {
515 return result;
516 }
517
518
519 BTreeHeader<K, V> newBtreeHeader = btreeHeader.copy();
520
521
522
523 if ( ( btreeType == BTreeTypeEnum.BTREE_OF_BTREES ) || ( btreeType == BTreeTypeEnum.COPIED_PAGES_BTREE ) )
524 {
525 PageIO[] pageIos = recordManager.readPageIOs( btreeHeader.getBTreeHeaderOffset(), -1L );
526
527 for ( PageIO pageIo : pageIos )
528 {
529 recordManager.freedPages.add( pageIo );
530 }
531 }
532
533 Page<K, V> newRootPage;
534
535 if ( result instanceof ModifyResult )
536 {
537 ModifyResult<K, V> modifyResult = ( ( ModifyResult<K, V> ) result );
538
539 newRootPage = modifyResult.getModifiedPage();
540
541
542 if ( modifyResult.getModifiedValue() == null )
543 {
544 newBtreeHeader.incrementNbElems();
545 }
546 }
547 else
548 {
549
550
551 SplitResult<K, V> splitResult = ( ( SplitResult<K, V> ) result );
552
553 K pivot = splitResult.getPivot();
554 Page<K, V> leftPage = splitResult.getLeftPage();
555 Page<K, V> rightPage = splitResult.getRightPage();
556
557
558
559 PageHolder<K, V> holderLeft = writePage( leftPage, revision );
560
561 PageHolder<K, V> holderRight = writePage( rightPage, revision );
562
563
564 newRootPage = new PersistedNode<K, V>( this, revision, pivot, holderLeft, holderRight );
565
566
567 newBtreeHeader.incrementNbElems();
568 }
569
570
571 LOG_PAGES.debug( "Writing the new rootPage revision {} for {}", revision, name );
572 writePage( newRootPage, revision );
573
574
575 newBtreeHeader.setRootPage( newRootPage );
576 newBtreeHeader.setRevision( revision );
577
578
579 long newBtreeHeaderOffset = recordManager.writeBtreeHeader( this, newBtreeHeader );
580
581
582 switch ( btreeType )
583 {
584 case PERSISTED :
585
586 recordManager.addInBtreeOfBtrees( getName(), revision, newBtreeHeaderOffset );
587
588 recordManager.addInCopiedPagesBtree( getName(), revision, result.getCopiedPages() );
589
590
591 storeRevision( newBtreeHeader, recordManager.isKeepRevisions() );
592
593 break;
594
595 case PERSISTED_SUB :
596
597 recordManager.addInCopiedPagesBtree( getName(), revision, result.getCopiedPages() );
598
599
600 storeRevision( newBtreeHeader, recordManager.isKeepRevisions() );
601
602 currentRevision.set( revision );
603
604 break;
605
606 case BTREE_OF_BTREES :
607
608 recordManager.updateRecordManagerHeader( newBtreeHeaderOffset, -1L );
609
610
611 recordManager.freePages( this, revision, result.getCopiedPages() );
612
613
614 storeRevision( newBtreeHeader, recordManager.isKeepRevisions() );
615
616 break;
617
618 case COPIED_PAGES_BTREE :
619
620 recordManager.updateRecordManagerHeader( -1L, newBtreeHeaderOffset );
621
622
623 recordManager.freePages( this, revision, result.getCopiedPages() );
624
625
626 storeRevision( newBtreeHeader, recordManager.isKeepRevisions() );
627
628 break;
629
630 default:
631
632 break;
633 }
634
635
636 return result;
637 }
638
639
640
641
642
643
644
645
646
647 private void writeBuffer( FileChannel channel, ByteBuffer bb, byte[] buffer ) throws IOException
648 {
649 int size = buffer.length;
650 int pos = 0;
651
652
653 do
654 {
655 if ( bb.remaining() >= size )
656 {
657
658 bb.put( buffer, pos, size );
659 size = 0;
660 }
661 else
662 {
663
664 int len = bb.remaining();
665 size -= len;
666 bb.put( buffer, pos, len );
667 pos += len;
668
669 bb.flip();
670
671 channel.write( bb );
672
673 bb.clear();
674 }
675 }
676 while ( size > 0 );
677 }
678
679
680
681
682
683
684 private PageHolder<K, V> writePage( Page<K, V> modifiedPage, long revision ) throws IOException
685 {
686 PageHolder<K, V> pageHolder = recordManager.writePage( this, modifiedPage, revision );
687
688 return pageHolder;
689 }
690
691
692
693
694
695
696
697
698
699 public Page<K, V> getRootPage( long revision ) throws IOException, KeyNotFoundException
700 {
701 return recordManager.getRootPage( this, revision );
702 }
703
704
705
706
707
708
709
710 public Page<K, V> getRootPage()
711 {
712 return getBTreeHeader( getName() ).getRootPage();
713 }
714
715
716 void setRootPage( Page<K, V> root )
717 {
718 getBTreeHeader( getName() ).setRootPage( root );
719 }
720
721
722
723
724
725 public long getBtreeInfoOffset()
726 {
727 return btreeInfoOffset;
728 }
729
730
731
732
733
734 public void setBtreeInfoOffset( long btreeInfoOffset )
735 {
736 this.btreeInfoOffset = btreeInfoOffset;
737 }
738
739
740
741
742
743 protected ReadTransaction<K, V> beginReadTransaction()
744 {
745 BTreeHeader<K, V> btreeHeader = getBTreeHeader( getName() );
746
747 ReadTransaction<K, V> readTransaction = new ReadTransaction<K, V>( recordManager, btreeHeader, readTransactions );
748
749 readTransactions.add( readTransaction );
750
751 return readTransaction;
752 }
753
754
755
756
757
758 protected ReadTransaction<K, V> beginReadTransaction( long revision )
759 {
760 BTreeHeader<K, V> btreeHeader = getBtreeHeader( revision );
761
762 if ( btreeHeader != null )
763 {
764 ReadTransaction<K, V> readTransaction = new ReadTransaction<K, V>( recordManager, btreeHeader, readTransactions );
765
766 readTransactions.add( readTransaction );
767
768 return readTransaction;
769 }
770 else
771 {
772 return null;
773 }
774 }
775
776
777
778
779
780 public String toString()
781 {
782 StringBuilder sb = new StringBuilder();
783
784 sb.append( "Managed BTree" );
785 sb.append( "[" ).append( getName() ).append( "]" );
786 sb.append( "( pageSize:" ).append( getPageSize() );
787
788 if ( getBTreeHeader( getName() ).getRootPage() != null )
789 {
790 sb.append( ", nbEntries:" ).append( getBTreeHeader( getName() ).getNbElems() );
791 }
792 else
793 {
794 sb.append( ", nbEntries:" ).append( 0 );
795 }
796
797 sb.append( ", comparator:" );
798
799 if ( keySerializer.getComparator() == null )
800 {
801 sb.append( "null" );
802 }
803 else
804 {
805 sb.append( keySerializer.getComparator().getClass().getSimpleName() );
806 }
807
808 sb.append( ", DuplicatesAllowed: " ).append( isAllowDuplicates() );
809
810 sb.append( ") : \n" );
811 sb.append( getBTreeHeader( getName() ).getRootPage().dumpPage( "" ) );
812
813 return sb.toString();
814 }
815 }