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