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.IOException;
24 import java.lang.reflect.Array;
25 import java.util.Comparator;
26 import java.util.Map;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.ConcurrentLinkedQueue;
29 import java.util.concurrent.atomic.AtomicBoolean;
30 import java.util.concurrent.atomic.AtomicLong;
31
32 import org.apache.directory.mavibot.btree.exception.BTreeCreationException;
33 import org.apache.directory.mavibot.btree.exception.DuplicateValueNotAllowedException;
34 import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
35 import org.apache.directory.mavibot.btree.serializer.ElementSerializer;
36
37
38
39
40
41
42
43
44
45
46
47 abstract class AbstractBTree<K, V> implements BTree<K, V>
48 {
49
50 protected long readTimeOut = DEFAULT_READ_TIMEOUT;
51
52
53 protected BTreeHeader<K, V> currentBtreeHeader;
54
55
56 protected ElementSerializer<K> keySerializer;
57
58
59 protected ElementSerializer<V> valueSerializer;
60
61
62 protected ConcurrentLinkedQueue<ReadTransaction<K, V>> readTransactions;
63
64
65 protected int writeBufferSize;
66
67
68 protected boolean allowDuplicates;
69
70
71 protected int pageSize;
72
73
74 protected String name;
75
76
77 protected String keySerializerFQCN;
78
79
80 protected String valueSerializerFQCN;
81
82
83 protected Thread readTransactionsThread;
84
85
86 protected BTreeTypeEnum btreeType;
87
88
89 protected AtomicBoolean transactionStarted = new AtomicBoolean( false );
90
91
92 protected Map<Long, BTreeHeader<K, V>> btreeRevisions = new ConcurrentHashMap<Long, BTreeHeader<K, V>>();
93
94
95 protected AtomicLong currentRevision = new AtomicLong( 0L );
96
97
98 protected TransactionManager transactionManager;
99
100
101
102
103
104
105
106 protected abstract ReadTransaction<K, V> beginReadTransaction();
107
108
109
110
111
112
113
114
115 protected abstract ReadTransaction<K, V> beginReadTransaction( long revision );
116
117
118
119
120
121 public TupleCursor<K, V> browse() throws IOException, KeyNotFoundException
122 {
123
124 if ( transactionManager == null )
125 {
126 throw new BTreeCreationException( "We don't have a transactionLManager" );
127 }
128
129 ReadTransaction<K, V> transaction = beginReadTransaction();
130
131 if ( transaction == null )
132 {
133 return new EmptyTupleCursor<K, V>( 0L );
134 }
135 else
136 {
137 ParentPos<K, V>[] stack = (ParentPos<K, V>[]) Array.newInstance( ParentPos.class, 32 );
138
139 TupleCursor<K, V> cursor = getRootPage().browse( transaction, stack, 0 );
140
141
142 cursor.beforeFirst();
143
144 return cursor;
145 }
146 }
147
148
149
150
151
152 public TupleCursor<K, V> browse( long revision ) throws IOException, KeyNotFoundException
153 {
154
155 if ( transactionManager == null )
156 {
157 throw new BTreeCreationException( "We don't have a transactionLManager" );
158 }
159
160 ReadTransaction<K, V> transaction = beginReadTransaction( revision );
161
162 if ( transaction == null )
163 {
164 return new EmptyTupleCursor<K, V>( revision );
165 }
166 else
167 {
168 ParentPos<K, V>[] stack = (ParentPos<K, V>[]) Array.newInstance( ParentPos.class, 32 );
169
170
171 TupleCursor<K, V> cursor = getRootPage( transaction.getRevision() ).browse( transaction, stack, 0 );
172
173 return cursor;
174 }
175 }
176
177
178
179
180
181 public TupleCursor<K, V> browseFrom( K key ) throws IOException
182 {
183
184 if ( transactionManager == null )
185 {
186 throw new BTreeCreationException( "We don't have a transactionLManager" );
187 }
188
189 ReadTransaction<K, V> transaction = beginReadTransaction();
190
191 ParentPos<K, V>[] stack = (ParentPos<K, V>[]) Array.newInstance( ParentPos.class, 32 );
192
193 TupleCursor<K, V> cursor;
194 try
195 {
196 cursor = getRootPage( transaction.getRevision() ).browse( key, transaction, stack, 0 );
197
198 return cursor;
199 }
200 catch ( KeyNotFoundException e )
201 {
202 throw new IOException( e.getMessage() );
203 }
204 }
205
206
207
208
209
210 public TupleCursor<K, V> browseFrom( long revision, K key ) throws IOException, KeyNotFoundException
211 {
212
213 if ( transactionManager == null )
214 {
215 throw new BTreeCreationException( "We don't have a transactionLManager" );
216 }
217
218 ReadTransaction<K, V> transaction = beginReadTransaction( revision );
219
220 if ( transaction == null )
221 {
222 return new EmptyTupleCursor<K, V>( revision );
223 }
224 else
225 {
226 ParentPos<K, V>[] stack = (ParentPos<K, V>[]) Array.newInstance( ParentPos.class, 32 );
227
228
229 TupleCursor<K, V> cursor = getRootPage( transaction.getRevision() ).browse( key, transaction, stack, 0 );
230
231 return cursor;
232 }
233 }
234
235
236
237
238
239 public boolean contains( K key, V value ) throws IOException
240 {
241
242 if ( transactionManager == null )
243 {
244 throw new BTreeCreationException( "We don't have a transactionLManager" );
245 }
246
247 ReadTransaction<K, V> transaction = beginReadTransaction();
248
249 if ( transaction == null )
250 {
251 return false;
252 }
253 else
254 {
255 try
256 {
257 return getRootPage( transaction.getRevision() ).contains( key, value );
258 }
259 catch ( KeyNotFoundException knfe )
260 {
261 throw new IOException( knfe.getMessage() );
262 }
263 finally
264 {
265 transaction.close();
266 }
267 }
268 }
269
270
271
272
273
274 public boolean contains( long revision, K key, V value ) throws IOException, KeyNotFoundException
275 {
276
277 if ( transactionManager == null )
278 {
279 throw new BTreeCreationException( "We don't have a transactionLManager" );
280 }
281
282
283 ReadTransaction<K, V> transaction = beginReadTransaction( revision );
284
285 if ( transaction == null )
286 {
287 return false;
288 }
289 else
290 {
291 try
292 {
293 return getRootPage( transaction.getRevision() ).contains( key, value );
294 }
295 finally
296 {
297 transaction.close();
298 }
299 }
300 }
301
302
303
304
305
306 public Tuple<K, V> delete( K key ) throws IOException
307 {
308
309 if ( transactionManager == null )
310 {
311 throw new BTreeCreationException( "We don't have a transactionLManager" );
312 }
313
314 if ( key == null )
315 {
316 throw new IllegalArgumentException( "Key must not be null" );
317 }
318
319
320 transactionManager.beginTransaction();
321
322 try
323 {
324 Tuple<K, V> deleted = delete( key, currentRevision.get() + 1 );
325
326
327 transactionManager.commit();
328
329 return deleted;
330 }
331 catch ( IOException ioe )
332 {
333
334 transactionManager.rollback();
335
336 return null;
337 }
338 }
339
340
341
342
343
344 public Tuple<K, V> delete( K key, V value ) throws IOException
345 {
346
347 if ( transactionManager == null )
348 {
349 throw new BTreeCreationException( "We don't have a transactionLManager" );
350 }
351
352 if ( key == null )
353 {
354 throw new IllegalArgumentException( "Key must not be null" );
355 }
356
357 if ( value == null )
358 {
359 throw new IllegalArgumentException( "Value must not be null" );
360 }
361
362 transactionManager.beginTransaction();
363
364 try
365 {
366 Tuple<K, V> deleted = delete( key, value, currentRevision.get() + 1 );
367
368 transactionManager.commit();
369
370 return deleted;
371 }
372 catch ( IOException ioe )
373 {
374 transactionManager.rollback();
375
376 throw ioe;
377 }
378 }
379
380
381
382
383
384
385
386
387
388 Tuple<K, V> delete( K key, long revision ) throws IOException
389 {
390 return delete( key, null, revision );
391 }
392
393
394 abstract Tuple<K, V> delete( K key, V value, long revision ) throws IOException;
395
396
397
398
399
400 public V insert( K key, V value ) throws IOException
401 {
402
403 if ( transactionManager == null )
404 {
405 throw new BTreeCreationException( "We don't have a transactionLManager" );
406 }
407
408 V existingValue = null;
409
410 if ( key == null )
411 {
412 throw new IllegalArgumentException( "Key must not be null" );
413 }
414
415
416
417 if ( btreeType != BTreeTypeEnum.PERSISTED_SUB )
418 {
419 transactionManager.beginTransaction();
420 }
421
422 try
423 {
424 InsertResult<K, V> result = insert( key, value, -1L );
425
426 if ( result instanceof ExistsResult )
427 {
428 existingValue = value;
429 }
430 else if ( result instanceof ModifyResult )
431 {
432 existingValue = ( ( ModifyResult<K, V> ) result ).getModifiedValue();
433 }
434
435
436 if ( btreeType != BTreeTypeEnum.PERSISTED_SUB )
437 {
438
439 transactionManager.commit();
440 }
441
442 return existingValue;
443 }
444 catch ( IOException ioe )
445 {
446
447
448 if ( btreeType != BTreeTypeEnum.PERSISTED_SUB )
449 {
450 transactionManager.rollback();
451 }
452
453 return null;
454 }
455 catch ( DuplicateValueNotAllowedException e )
456 {
457
458
459 if ( btreeType != BTreeTypeEnum.PERSISTED_SUB )
460 {
461 transactionManager.rollback();
462 }
463
464 throw e;
465 }
466 }
467
468
469
470
471
472 abstract InsertResult<K, V> insert( K key, V value, long revision ) throws IOException;
473
474
475
476
477
478
479 public void flush() throws IOException
480 {
481 }
482
483
484
485
486
487 public V get( K key ) throws IOException, KeyNotFoundException
488 {
489
490 if ( transactionManager == null )
491 {
492 throw new BTreeCreationException( "We don't have a transactionLManager" );
493 }
494
495 ReadTransaction<K, V> transaction = beginReadTransaction();
496
497 if ( transaction == null )
498 {
499 return null;
500 }
501 else
502 {
503 try
504 {
505 return getRootPage( transaction.getRevision() ).get( key );
506 }
507 finally
508 {
509 transaction.close();
510 }
511 }
512 }
513
514
515
516
517
518 public V get( long revision, K key ) throws IOException, KeyNotFoundException
519 {
520
521 if ( transactionManager == null )
522 {
523 throw new BTreeCreationException( "We don't have a transactionLManager" );
524 }
525
526 ReadTransaction<K, V> transaction = beginReadTransaction( revision );
527
528 if ( transaction == null )
529 {
530 return null;
531 }
532 else
533 {
534 try
535 {
536 return getRootPage( transaction.getRevision() ).get( key );
537 }
538 finally
539 {
540 transaction.close();
541 }
542 }
543 }
544
545
546
547
548
549 public abstract Page<K, V> getRootPage();
550
551
552
553
554
555 abstract void setRootPage( Page<K, V> root );
556
557
558
559
560
561 public ValueCursor<V> getValues( K key ) throws IOException, KeyNotFoundException
562 {
563
564 if ( transactionManager == null )
565 {
566 throw new BTreeCreationException( "We don't have a transactionLManager" );
567 }
568
569 ReadTransaction<K, V> transaction = beginReadTransaction();
570
571 if ( transaction == null )
572 {
573 return new EmptyValueCursor<V>( 0L );
574 }
575 else
576 {
577 try
578 {
579 return getRootPage( transaction.getRevision() ).getValues( key );
580 }
581 finally
582 {
583 transaction.close();
584 }
585 }
586 }
587
588
589
590
591
592 public boolean hasKey( K key ) throws IOException, KeyNotFoundException
593 {
594
595 if ( transactionManager == null )
596 {
597 throw new BTreeCreationException( "We don't have a transactionLManager" );
598 }
599
600 if ( key == null )
601 {
602 return false;
603 }
604
605 ReadTransaction<K, V> transaction = beginReadTransaction();
606
607 if ( transaction == null )
608 {
609 return false;
610 }
611 else
612 {
613 try
614 {
615 return getRootPage( transaction.getRevision() ).hasKey( key );
616 }
617 finally
618 {
619 transaction.close();
620 }
621 }
622 }
623
624
625
626
627
628 public boolean hasKey( long revision, K key ) throws IOException, KeyNotFoundException
629 {
630
631 if ( transactionManager == null )
632 {
633 throw new BTreeCreationException( "We don't have a transactionLManager" );
634 }
635
636 if ( key == null )
637 {
638 return false;
639 }
640
641 ReadTransaction<K, V> transaction = beginReadTransaction( revision );
642
643 if ( transaction == null )
644 {
645 return false;
646 }
647 else
648 {
649 try
650 {
651 return getRootPage( transaction.getRevision() ).hasKey( key );
652 }
653 finally
654 {
655 transaction.close();
656 }
657 }
658 }
659
660
661
662
663
664 public ElementSerializer<K> getKeySerializer()
665 {
666 return keySerializer;
667 }
668
669
670
671
672
673 public void setKeySerializer( ElementSerializer<K> keySerializer )
674 {
675 this.keySerializer = keySerializer;
676 keySerializerFQCN = keySerializer.getClass().getName();
677 }
678
679
680
681
682
683 public String getKeySerializerFQCN()
684 {
685 return keySerializerFQCN;
686 }
687
688
689
690
691
692 public ElementSerializer<V> getValueSerializer()
693 {
694 return valueSerializer;
695 }
696
697
698
699
700
701 public void setValueSerializer( ElementSerializer<V> valueSerializer )
702 {
703 this.valueSerializer = valueSerializer;
704 valueSerializerFQCN = valueSerializer.getClass().getName();
705 }
706
707
708
709
710
711 public String getValueSerializerFQCN()
712 {
713 return valueSerializerFQCN;
714 }
715
716
717
718
719
720 public long getRevision()
721 {
722
723 if ( transactionManager == null )
724 {
725 throw new BTreeCreationException( "We don't have a transactionLManager" );
726 }
727
728 ReadTransaction<K, V> transaction = beginReadTransaction();
729
730 if ( transaction == null )
731 {
732 return -1L;
733 }
734 else
735 {
736 try
737 {
738 return transaction.getRevision();
739 }
740 finally
741 {
742 transaction.close();
743 }
744 }
745 }
746
747
748
749
750
751 void setRevision( long revision )
752 {
753 transactionManager.getBTreeHeader( getName() ).setRevision( revision );
754 }
755
756
757
758
759
760 protected void storeRevision( BTreeHeader<K, V> btreeHeader, boolean keepRevisions )
761 {
762 long revision = btreeHeader.getRevision();
763
764 if ( keepRevisions )
765 {
766 synchronized ( btreeRevisions )
767 {
768 btreeRevisions.put( revision, btreeHeader );
769 }
770 }
771
772 currentRevision.set( revision );
773 currentBtreeHeader = btreeHeader;
774
775
776 if ( btreeHeader.getBtree().getType() != BTreeTypeEnum.PERSISTED_SUB )
777 {
778 transactionManager.updateNewBTreeHeaders( btreeHeader );
779 }
780 }
781
782
783
784
785
786 protected void storeRevision( BTreeHeader<K, V> btreeHeader )
787 {
788 long revision = btreeHeader.getRevision();
789
790 synchronized ( btreeRevisions )
791 {
792 btreeRevisions.put( revision, btreeHeader );
793 }
794
795 currentRevision.set( revision );
796 currentBtreeHeader = btreeHeader;
797
798
799 if ( btreeHeader.getBtree().getType() != BTreeTypeEnum.PERSISTED_SUB )
800 {
801 transactionManager.updateNewBTreeHeaders( btreeHeader );
802 }
803 }
804
805
806
807
808
809 public long getReadTimeOut()
810 {
811 return readTimeOut;
812 }
813
814
815
816
817
818 public void setReadTimeOut( long readTimeOut )
819 {
820 this.readTimeOut = readTimeOut;
821 }
822
823
824
825
826
827 public long getNbElems()
828 {
829
830 if ( transactionManager == null )
831 {
832 throw new BTreeCreationException( "We don't have a transactionLManager" );
833 }
834
835 ReadTransaction<K, V> transaction = beginReadTransaction();
836
837 if ( transaction == null )
838 {
839 return -1L;
840 }
841 else
842 {
843 try
844 {
845 return transaction.getBtreeHeader().getNbElems();
846 }
847 finally
848 {
849 transaction.close();
850 }
851 }
852 }
853
854
855
856
857
858 void setNbElems( long nbElems )
859 {
860 transactionManager.getBTreeHeader( getName() ).setNbElems( nbElems );
861 }
862
863
864
865
866
867 public int getPageSize()
868 {
869 return pageSize;
870 }
871
872
873
874
875
876 public void setPageSize( int pageSize )
877 {
878 if ( pageSize <= 2 )
879 {
880 this.pageSize = DEFAULT_PAGE_SIZE;
881 }
882 else
883 {
884 this.pageSize = getPowerOf2( pageSize );
885 }
886 }
887
888
889
890
891
892 public String getName()
893 {
894 return name;
895 }
896
897
898
899
900
901 public void setName( String name )
902 {
903 this.name = name;
904 }
905
906
907
908
909
910 public Comparator<K> getComparator()
911 {
912 return keySerializer.getComparator();
913 }
914
915
916
917
918
919 public int getWriteBufferSize()
920 {
921 return writeBufferSize;
922 }
923
924
925
926
927
928 public void setWriteBufferSize( int writeBufferSize )
929 {
930 this.writeBufferSize = writeBufferSize;
931 }
932
933
934
935
936
937 public boolean isAllowDuplicates()
938 {
939 return allowDuplicates;
940 }
941
942
943
944
945
946 public void setAllowDuplicates( boolean allowDuplicates )
947 {
948 this.allowDuplicates = allowDuplicates;
949 }
950
951
952
953
954
955 public BTreeTypeEnum getType()
956 {
957 return btreeType;
958 }
959
960
961
962
963
964 public void setType( BTreeTypeEnum type )
965 {
966 this.btreeType = type;
967 }
968
969
970
971
972
973 private int getPowerOf2( int size )
974 {
975 int newSize = --size;
976 newSize |= newSize >> 1;
977 newSize |= newSize >> 2;
978 newSize |= newSize >> 4;
979 newSize |= newSize >> 8;
980 newSize |= newSize >> 16;
981 newSize++;
982
983 return newSize;
984 }
985
986
987
988
989
990 protected BTreeHeader<K, V> getBtreeHeader()
991 {
992 return currentBtreeHeader;
993 }
994
995
996
997
998
999 protected BTreeHeader<K, V> getBtreeHeader( long revision )
1000 {
1001 return btreeRevisions.get( revision );
1002 }
1003
1004
1005
1006
1007
1008 public KeyCursor<K> browseKeys() throws IOException, KeyNotFoundException
1009 {
1010
1011 if ( transactionManager == null )
1012 {
1013 throw new BTreeCreationException( "We don't have a Transaction Manager" );
1014 }
1015
1016 ReadTransaction transaction = beginReadTransaction();
1017
1018 if ( transaction == null )
1019 {
1020 return new KeyCursor<K>();
1021 }
1022 else
1023 {
1024 ParentPos<K, K>[] stack = (ParentPos<K, K>[]) Array.newInstance( ParentPos.class, 32 );
1025
1026 KeyCursor<K> cursor = getRootPage().browseKeys( transaction, stack, 0 );
1027
1028
1029 cursor.beforeFirst();
1030
1031 return cursor;
1032 }
1033 }
1034
1035
1036
1037
1038
1039
1040 void createTransactionManager()
1041 {
1042 Runnable readTransactionTask = new Runnable()
1043 {
1044 public void run()
1045 {
1046 try
1047 {
1048 ReadTransaction<K, V> transaction = null;
1049
1050 while ( !Thread.currentThread().isInterrupted() )
1051 {
1052 long timeoutDate = System.currentTimeMillis() - readTimeOut;
1053 long t0 = System.currentTimeMillis();
1054 int nbTxns = 0;
1055
1056
1057 while ( ( transaction = readTransactions.peek() ) != null )
1058 {
1059 nbTxns++;
1060
1061 if ( transaction.isClosed() )
1062 {
1063
1064 readTransactions.poll();
1065 continue;
1066 }
1067
1068
1069 if ( transaction.getCreationDate() < timeoutDate )
1070 {
1071 transaction.close();
1072 readTransactions.poll();
1073
1074 synchronized ( btreeRevisions )
1075 {
1076 btreeRevisions.remove( transaction.getRevision() );
1077 }
1078
1079 continue;
1080 }
1081
1082
1083 break;
1084 }
1085
1086 long t1 = System.currentTimeMillis();
1087
1088 if ( nbTxns > 0 )
1089 {
1090 System.out.println( "Processing old txn : " + nbTxns + ", " + ( t1 - t0 ) + "ms" );
1091 }
1092
1093
1094 Thread.sleep( readTimeOut );
1095 }
1096 }
1097 catch ( InterruptedException ie )
1098 {
1099
1100 }
1101 catch ( Exception e )
1102 {
1103 throw new RuntimeException( e );
1104 }
1105 }
1106 };
1107
1108 readTransactionsThread = new Thread( readTransactionTask );
1109 readTransactionsThread.setDaemon( true );
1110 readTransactionsThread.start();
1111 }
1112 }