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.File;
24 import java.io.IOException;
25 import java.io.RandomAccessFile;
26 import java.nio.ByteBuffer;
27 import java.nio.channels.FileChannel;
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.LinkedHashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Queue;
35 import java.util.Set;
36 import java.util.concurrent.LinkedBlockingQueue;
37 import java.util.concurrent.atomic.AtomicLong;
38 import java.util.concurrent.locks.ReadWriteLock;
39 import java.util.concurrent.locks.ReentrantLock;
40 import java.util.concurrent.locks.ReentrantReadWriteLock;
41
42 import org.apache.directory.mavibot.btree.exception.BTreeAlreadyManagedException;
43 import org.apache.directory.mavibot.btree.exception.BTreeCreationException;
44 import org.apache.directory.mavibot.btree.exception.EndOfFileExceededException;
45 import org.apache.directory.mavibot.btree.exception.FileException;
46 import org.apache.directory.mavibot.btree.exception.InvalidOffsetException;
47 import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
48 import org.apache.directory.mavibot.btree.exception.RecordManagerException;
49 import org.apache.directory.mavibot.btree.serializer.ElementSerializer;
50 import org.apache.directory.mavibot.btree.serializer.IntSerializer;
51 import org.apache.directory.mavibot.btree.serializer.LongArraySerializer;
52 import org.apache.directory.mavibot.btree.serializer.LongSerializer;
53 import org.apache.directory.mavibot.btree.util.Strings;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57
58
59
60
61
62
63
64
65
66
67 public class RecordManager extends AbstractTransactionManager
68 {
69
70 protected static final Logger LOG = LoggerFactory.getLogger( RecordManager.class );
71
72
73 protected static final Logger TXN_LOG = LoggerFactory.getLogger( "TXN_LOG" );
74
75
76 protected static final Logger LOG_PAGES = LoggerFactory.getLogger( "org.apache.directory.mavibot.LOG_PAGES" );
77
78
79 protected static final Logger LOG_CHECK = LoggerFactory.getLogger( "org.apache.directory.mavibot.LOG_CHECK" );
80
81
82 private File file;
83
84
85 FileChannel fileChannel;
86
87
88 int nbBtree;
89
90
91 long firstFreePage;
92
93
94 public AtomicLong nbFreedPages = new AtomicLong( 0 );
95 public AtomicLong nbCreatedPages = new AtomicLong( 0 );
96 public AtomicLong nbReusedPages = new AtomicLong( 0 );
97 public AtomicLong nbUpdateRMHeader = new AtomicLong( 0 );
98 public AtomicLong nbUpdateBtreeHeader = new AtomicLong( 0 );
99 public AtomicLong nbUpdatePageIOs = new AtomicLong( 0 );
100
101
102 private long endOfFileOffset;
103
104
105
106
107
108 BTree<RevisionName, long[]> copiedPageBtree;
109
110
111 public static final long NO_PAGE = -1L;
112
113
114 private static final int PAGE_SIZE = 4;
115
116
117 private static final int LINK_SIZE = 8;
118
119
120 private static final int BYTE_SIZE = 1;
121 static final int INT_SIZE = 4;
122 static final int LONG_SIZE = 8;
123
124
125 public static final int DEFAULT_PAGE_SIZE = 512;
126
127
128 private static final int MIN_PAGE_SIZE = 64;
129
130
131 static int RECORD_MANAGER_HEADER_SIZE = DEFAULT_PAGE_SIZE;
132
133
134 private ByteBuffer RECORD_MANAGER_HEADER_BUFFER;
135
136
137 private byte[] RECORD_MANAGER_HEADER_BYTES;
138
139
140 private byte[] LONG_LENGTH = new byte[]
141 { ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xF8 };
142
143
144 int pageSize = DEFAULT_PAGE_SIZE;
145
146
147 private Map<String, BTree<Object, Object>> managedBtrees;
148
149
150 private Queue<RevisionName> closedTransactionsQueue = new LinkedBlockingQueue<RevisionName>();
151
152
153 private static final String DEFAULT_FILE_NAME = "mavibot.db";
154
155
156 private boolean keepRevisions;
157
158
159 public static final boolean INTERNAL_BTREE = true;
160
161
162 public static final boolean NORMAL_BTREE = false;
163
164
165 BTree<NameRevision, Long> btreeOfBtrees;
166
167
168 static final String BTREE_OF_BTREES_NAME = "_btree_of_btrees_";
169
170
171 static final String COPIED_PAGE_BTREE_NAME = "_copiedPageBtree_";
172
173
174 long currentBtreeOfBtreesOffset;
175
176
177 private long previousBtreeOfBtreesOffset = NO_PAGE;
178
179
180 long currentCopiedPagesBtreeOffset = NO_PAGE;
181
182
183 private long previousCopiedPagesBtreeOffset = NO_PAGE;
184
185
186 private ReentrantLock transactionLock = new ReentrantLock();
187
188
189 private static final ThreadLocal<Integer> context = new ThreadLocal<Integer>();
190
191
192 List<PageIO> freedPages = new ArrayList<PageIO>();
193
194
195 private List<PageIO> allocatedPages = new ArrayList<PageIO>();
196
197
198 private Map<String, BTreeHeader<?, ?>> currentBTreeHeaders = new HashMap<String, BTreeHeader<?, ?>>();
199
200
201 private Map<String, BTreeHeader<?, ?>> newBTreeHeaders = new HashMap<String, BTreeHeader<?, ?>>();
202
203
204 private ReadWriteLock btreeHeadersLock = new ReentrantReadWriteLock();
205
206
207 private static final int ROLLBACKED_TXN = 0;
208
209
210 private ReentrantLock freePageLock = new ReentrantLock();
211
212
213 private PageReclaimer reclaimer;
214
215
216 private int commitCount = 0;
217
218
219
220
221 private int pageReclaimerThreshold = 70;
222
223
224 private boolean disableReclaimer = false;
225
226 public Map<Long, Integer> writeCounter = new HashMap<Long, Integer>();
227
228
229
230
231
232
233
234
235
236 public RecordManager( String fileName )
237 {
238 this( fileName, DEFAULT_PAGE_SIZE );
239 }
240
241
242
243
244
245
246
247
248
249
250 public RecordManager( String fileName, int pageSize )
251 {
252 managedBtrees = new LinkedHashMap<String, BTree<Object, Object>>();
253
254 if ( pageSize < MIN_PAGE_SIZE )
255 {
256 this.pageSize = MIN_PAGE_SIZE;
257 }
258 else
259 {
260 this.pageSize = pageSize;
261 }
262
263 RECORD_MANAGER_HEADER_BUFFER = ByteBuffer.allocate( this.pageSize );
264 RECORD_MANAGER_HEADER_BYTES = new byte[this.pageSize];
265 RECORD_MANAGER_HEADER_SIZE = this.pageSize;
266
267
268 File tmpFile = new File( fileName );
269
270 if ( tmpFile.isDirectory() )
271 {
272
273 tmpFile = new File( tmpFile, DEFAULT_FILE_NAME );
274 }
275
276
277 boolean isNewFile = createFile( tmpFile );
278
279 try
280 {
281 RandomAccessFile randomFile = new RandomAccessFile( file, "rw" );
282 fileChannel = randomFile.getChannel();
283
284
285 endOfFileOffset = fileChannel.size();
286
287 if ( isNewFile )
288 {
289 initRecordManager();
290 }
291 else
292 {
293 loadRecordManager();
294 }
295
296 reclaimer = new PageReclaimer( this );
297 runReclaimer();
298 }
299 catch ( Exception e )
300 {
301 LOG.error( "Error while initializing the RecordManager : {}", e.getMessage() );
302 LOG.error( "", e );
303 throw new RecordManagerException( e );
304 }
305 }
306
307
308
309
310
311 private void runReclaimer()
312 {
313 if ( disableReclaimer )
314 {
315 LOG.warn( "Free page reclaimer is disabled, this should not be disabled on production systems." );
316 return;
317 }
318
319 try
320 {
321 commitCount = 0;
322 reclaimer.reclaim();
323
324 updateRecordManagerHeader();
325 }
326 catch ( Exception e )
327 {
328 LOG.warn( "PageReclaimer failed to free the pages", e );
329 }
330 }
331
332
333
334
335
336 private boolean createFile( File mavibotFile )
337 {
338 try
339 {
340 boolean creation = mavibotFile.createNewFile();
341
342 file = mavibotFile;
343
344 if ( mavibotFile.length() == 0 )
345 {
346 return true;
347 }
348 else
349 {
350 return creation;
351 }
352 }
353 catch ( IOException ioe )
354 {
355 LOG.error( "Cannot create the file {}", mavibotFile.getName() );
356 return false;
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 private void initRecordManager() throws IOException
391 {
392
393 nbBtree = 0;
394 firstFreePage = NO_PAGE;
395 currentBtreeOfBtreesOffset = NO_PAGE;
396
397 updateRecordManagerHeader();
398
399
400 endOfFileOffset = fileChannel.size();
401
402
403 createBtreeOfBtrees();
404
405
406 createCopiedPagesBtree();
407
408
409 try
410 {
411 manageSubBtree( btreeOfBtrees );
412
413 currentBtreeOfBtreesOffset = ( ( PersistedBTree<NameRevision, Long> ) btreeOfBtrees ).getBtreeHeader()
414 .getBTreeHeaderOffset();
415 updateRecordManagerHeader();
416
417
418 currentBTreeHeaders.put( BTREE_OF_BTREES_NAME,
419 ( ( PersistedBTree<NameRevision, Long> ) btreeOfBtrees ).getBtreeHeader() );
420 newBTreeHeaders.put( BTREE_OF_BTREES_NAME,
421 ( ( PersistedBTree<NameRevision, Long> ) btreeOfBtrees ).getBtreeHeader() );
422
423
424 manageSubBtree( copiedPageBtree );
425
426 currentCopiedPagesBtreeOffset = ( ( PersistedBTree<RevisionName, long[]> ) copiedPageBtree ).getBtreeHeader().getBTreeHeaderOffset();
427 updateRecordManagerHeader();
428
429
430 currentBTreeHeaders.put( COPIED_PAGE_BTREE_NAME, ( ( PersistedBTree<RevisionName, long[]> ) copiedPageBtree ).getBtreeHeader() );
431 newBTreeHeaders.put( COPIED_PAGE_BTREE_NAME, ( ( PersistedBTree<RevisionName, long[]> ) copiedPageBtree ).getBtreeHeader() );
432 }
433 catch ( BTreeAlreadyManagedException btame )
434 {
435
436 }
437
438
439 if ( LOG_CHECK.isDebugEnabled() )
440 {
441 MavibotInspector.check( this );
442 }
443
444 }
445
446
447
448
449
450 private void createBtreeOfBtrees()
451 {
452 PersistedBTreeConfiguration<NameRevision, Long> configuration = new PersistedBTreeConfiguration<NameRevision, Long>();
453 configuration.setKeySerializer( NameRevisionSerializer.INSTANCE );
454 configuration.setName( BTREE_OF_BTREES_NAME );
455 configuration.setValueSerializer( LongSerializer.INSTANCE );
456 configuration.setBtreeType( BTreeTypeEnum.BTREE_OF_BTREES );
457 configuration.setCacheSize( PersistedBTree.DEFAULT_CACHE_SIZE );
458
459 btreeOfBtrees = BTreeFactory.createPersistedBTree( configuration );
460 }
461
462
463
464
465
466 private void createCopiedPagesBtree()
467 {
468 PersistedBTreeConfiguration<RevisionName, long[]> configuration = new PersistedBTreeConfiguration<RevisionName, long[]>();
469 configuration.setKeySerializer( RevisionNameSerializer.INSTANCE );
470 configuration.setName( COPIED_PAGE_BTREE_NAME );
471 configuration.setValueSerializer( LongArraySerializer.INSTANCE );
472 configuration.setBtreeType( BTreeTypeEnum.COPIED_PAGES_BTREE );
473 configuration.setCacheSize( PersistedBTree.DEFAULT_CACHE_SIZE );
474
475 copiedPageBtree = BTreeFactory.createPersistedBTree( configuration );
476 }
477
478
479
480
481
482
483
484
485
486
487
488
489 private void loadRecordManager() throws IOException, ClassNotFoundException, IllegalAccessException,
490 InstantiationException, IllegalArgumentException, SecurityException, NoSuchFieldException, KeyNotFoundException
491 {
492 if ( fileChannel.size() != 0 )
493 {
494 ByteBuffer recordManagerHeader = ByteBuffer.allocate( RECORD_MANAGER_HEADER_SIZE );
495
496
497 fileChannel.read( recordManagerHeader );
498
499 recordManagerHeader.rewind();
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519 pageSize = recordManagerHeader.getInt();
520
521
522 nbBtree = recordManagerHeader.getInt();
523
524
525 firstFreePage = recordManagerHeader.getLong();
526
527
528 checkFreePages();
529
530
531 currentBtreeOfBtreesOffset = recordManagerHeader.getLong();
532
533
534 previousBtreeOfBtreesOffset = recordManagerHeader.getLong();
535
536
537 currentCopiedPagesBtreeOffset = recordManagerHeader.getLong();
538
539
540 previousCopiedPagesBtreeOffset = recordManagerHeader.getLong();
541
542
543 PageIO[] bobHeaderPageIos = readPageIOs( currentBtreeOfBtreesOffset, Long.MAX_VALUE );
544
545 btreeOfBtrees = BTreeFactory.<NameRevision, Long> createPersistedBTree( BTreeTypeEnum.BTREE_OF_BTREES );
546
547
548 loadBtree( bobHeaderPageIos, btreeOfBtrees );
549
550
551 PageIO[] copiedPagesPageIos = readPageIOs( currentCopiedPagesBtreeOffset, Long.MAX_VALUE );
552
553 copiedPageBtree = BTreeFactory.<RevisionName, long[]> createPersistedBTree( BTreeTypeEnum.COPIED_PAGES_BTREE );
554
555
556 loadBtree( copiedPagesPageIos, copiedPageBtree );
557
558
559 TupleCursor<NameRevision, Long> btreeCursor = btreeOfBtrees.browse();
560 Map<String, Long> loadedBtrees = new HashMap<String, Long>();
561
562
563 long currentRevision = -1L;
564
565 while ( btreeCursor.hasNext() )
566 {
567 Tuple<NameRevision, Long> btreeTuple = btreeCursor.next();
568 NameRevision nameRevision = btreeTuple.getKey();
569 long btreeOffset = btreeTuple.getValue();
570 long revision = nameRevision.getValue();
571
572
573 Long loadedBtreeRevision = loadedBtrees.get( nameRevision.getName() );
574
575 if ( loadedBtreeRevision != null )
576 {
577
578 if ( revision > currentRevision )
579 {
580
581 loadedBtrees.put( nameRevision.getName(), btreeOffset );
582 currentRevision = revision;
583 }
584 }
585 else
586 {
587
588 loadedBtrees.put( nameRevision.getName(), btreeOffset );
589 currentRevision = nameRevision.getRevision();
590 }
591 }
592
593
594
595
596
597 for ( String btreeName : loadedBtrees.keySet() )
598 {
599 long btreeOffset = loadedBtrees.get( btreeName );
600
601 PageIO[] btreePageIos = readPageIOs( btreeOffset, Long.MAX_VALUE );
602
603 BTree<?, ?> btree = BTreeFactory.<NameRevision, Long> createPersistedBTree();
604
605 loadBtree( btreePageIos, btree );
606
607
608 managedBtrees.put( btreeName, ( BTree<Object, Object> ) btree );
609 }
610
611
612 endOfFileOffset = fileChannel.size();
613 }
614 }
615
616
617
618
619
620 public void beginTransaction()
621 {
622 if ( TXN_LOG.isDebugEnabled() )
623 {
624 TXN_LOG.debug( "Begining a new transaction on thread {}, TxnLevel {}",
625 Thread.currentThread().getName(), getTxnLevel() );
626 }
627
628
629 if ( !( ( ReentrantLock ) transactionLock ).isHeldByCurrentThread() )
630 {
631 TXN_LOG.debug( "--> Lock taken" );
632 transactionLock.lock();
633 }
634 else
635 {
636 TXN_LOG.debug( "..o The current thread already holds the lock" );
637 }
638
639
640 incrementTxnLevel();
641 }
642
643
644
645
646
647 public void commit()
648 {
649
650 if ( !transactionLock.isHeldByCurrentThread() )
651 {
652 String name = Thread.currentThread().getName();
653 String err = "This thread, '" + name + "' does not hold the transactionLock ";
654 TXN_LOG.error( err );
655 throw new RecordManagerException( err );
656 }
657
658 if ( TXN_LOG.isDebugEnabled() )
659 {
660 TXN_LOG.debug( "Committing a transaction on thread {}, TxnLevel {}",
661 Thread.currentThread().getName(), getTxnLevel() );
662 }
663
664 if ( !fileChannel.isOpen() )
665 {
666
667 int txnLevel = decrementTxnLevel();
668
669 if ( txnLevel == 0 )
670 {
671
672
673 transactionLock.unlock();
674 }
675
676 return;
677 }
678
679 int nbTxnStarted = context.get();
680
681 switch ( nbTxnStarted )
682 {
683 case ROLLBACKED_TXN:
684
685 transactionLock.unlock();
686
687 return;
688
689 case 1:
690
691
692 updateRecordManagerHeader();
693
694
695 swapCurrentBtreeHeaders();
696
697
698 for ( PageIO pageIo : freedPages )
699 {
700 try
701 {
702 free( pageIo );
703 }
704 catch ( IOException ioe )
705 {
706 throw new RecordManagerException( ioe.getMessage() );
707 }
708 }
709
710
711 freedPages.clear();
712 allocatedPages.clear();
713
714
715
716 updateRecordManagerHeader();
717
718 commitCount++;
719
720 if ( commitCount >= pageReclaimerThreshold )
721 {
722 runReclaimer();
723 }
724
725
726
727 int txnLevel = decrementTxnLevel();
728
729 if ( txnLevel == 0 )
730 {
731 transactionLock.unlock();
732 }
733
734 return;
735
736 default:
737
738
739 updateRecordManagerHeader();
740
741
742
743
744
745 for ( PageIO pageIo : freedPages )
746 {
747 try
748 {
749 free( pageIo );
750 }
751 catch ( IOException ioe )
752 {
753 throw new RecordManagerException( ioe.getMessage() );
754 }
755 }
756
757
758 freedPages.clear();
759 allocatedPages.clear();
760
761
762
763 updateRecordManagerHeader();
764
765 commitCount++;
766
767 if ( commitCount >= pageReclaimerThreshold )
768 {
769 runReclaimer();
770 }
771
772
773
774 txnLevel = decrementTxnLevel();
775
776 if ( txnLevel == 0 )
777 {
778 transactionLock.unlock();
779 }
780
781 return;
782 }
783 }
784
785
786 public boolean isContextOk()
787 {
788 return ( context == null ? true : ( context.get() == 0 ) );
789 }
790
791
792
793
794
795 private int getTxnLevel()
796 {
797 Integer nbTxnLevel = context.get();
798
799 if ( nbTxnLevel == null )
800 {
801 return -1;
802 }
803
804 return nbTxnLevel;
805 }
806
807
808
809
810
811 private void incrementTxnLevel()
812 {
813 Integer nbTxnLevel = context.get();
814
815 if ( nbTxnLevel == null )
816 {
817 context.set( 1 );
818 }
819 else
820 {
821
822 context.set( nbTxnLevel + 1 );
823 }
824
825 if ( TXN_LOG.isDebugEnabled() )
826 {
827 TXN_LOG.debug( "Incrementing the TxnLevel : {}", context.get() );
828 }
829 }
830
831
832
833
834
835 private int decrementTxnLevel()
836 {
837 int nbTxnStarted = context.get() - 1;
838
839 context.set( nbTxnStarted );
840
841 if ( TXN_LOG.isDebugEnabled() )
842 {
843 TXN_LOG.debug( "Decrementing the TxnLevel : {}", context.get() );
844 }
845
846 return nbTxnStarted;
847 }
848
849
850
851
852
853 public void rollback()
854 {
855
856 if ( !transactionLock.isHeldByCurrentThread() )
857 {
858 TXN_LOG.error( "This thread does not hold the transactionLock" );
859 throw new RecordManagerException( "This thread does not hold the transactionLock" );
860 }
861
862 if ( TXN_LOG.isDebugEnabled() )
863 {
864 TXN_LOG.debug( "Rollbacking a new transaction on thread {}, TxnLevel {}",
865 Thread.currentThread().getName(), getTxnLevel() );
866 }
867
868
869 context.set( ROLLBACKED_TXN );
870
871
872 for ( PageIO pageIo : allocatedPages )
873 {
874 try
875 {
876 free( pageIo );
877 }
878 catch ( IOException ioe )
879 {
880 throw new RecordManagerException( ioe.getMessage() );
881 }
882 }
883
884
885 freedPages.clear();
886 allocatedPages.clear();
887
888
889 updateRecordManagerHeader();
890
891
892 revertBtreeHeaders();
893
894
895
896 transactionLock.unlock();
897 }
898
899
900
901
902
903
904
905
906
907
908 PageIO[] readPageIOs( long position, long limit ) throws IOException, EndOfFileExceededException
909 {
910 LOG.debug( "Read PageIOs at position {}", position );
911
912 if ( limit <= 0 )
913 {
914 limit = Long.MAX_VALUE;
915 }
916
917 PageIO firstPage = fetchPage( position );
918 firstPage.setSize();
919 List<PageIO> listPages = new ArrayList<PageIO>();
920 listPages.add( firstPage );
921 long dataRead = pageSize - LONG_SIZE - INT_SIZE;
922
923
924 long nextPage = firstPage.getNextPage();
925
926 if ( ( dataRead < limit ) && ( nextPage != NO_PAGE ) )
927 {
928 while ( dataRead < limit )
929 {
930 PageIO page = fetchPage( nextPage );
931 listPages.add( page );
932 nextPage = page.getNextPage();
933 dataRead += pageSize - LONG_SIZE;
934
935 if ( nextPage == NO_PAGE )
936 {
937 page.setNextPage( NO_PAGE );
938 break;
939 }
940 }
941 }
942
943 LOG.debug( "Nb of PageIOs read : {}", listPages.size() );
944
945
946 return listPages.toArray( new PageIO[]
947 {} );
948 }
949
950
951
952
953
954
955
956
957
958
959
960
961 void checkOffset( long offset )
962 {
963 if ( ( offset < 0 ) || ( offset > endOfFileOffset ) || ( ( offset % pageSize ) != 0 ) )
964 {
965 throw new InvalidOffsetException( "Bad Offset : " + offset );
966 }
967 }
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983 private <K, V> void loadBtree( PageIO[] pageIos, BTree<K, V> btree ) throws EndOfFileExceededException,
984 IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, IllegalArgumentException,
985 SecurityException, NoSuchFieldException
986 {
987 loadBtree( pageIos, btree, null );
988 }
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004 <K, V> void loadBtree( PageIO[] pageIos, BTree btree, BTree<K, V> parentBTree )
1005 throws EndOfFileExceededException,
1006 IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, IllegalArgumentException,
1007 SecurityException, NoSuchFieldException
1008 {
1009 long dataPos = 0L;
1010
1011
1012 BTreeHeader<K, V> btreeHeader = new BTreeHeader<K, V>();
1013 btreeHeader.setBtree( btree );
1014
1015
1016 btreeHeader.setBTreeHeaderOffset( pageIos[0].getOffset() );
1017
1018
1019 long revision = readLong( pageIos, dataPos );
1020 btreeHeader.setRevision( revision );
1021 dataPos += LONG_SIZE;
1022
1023
1024 long nbElems = readLong( pageIos, dataPos );
1025 btreeHeader.setNbElems( nbElems );
1026 dataPos += LONG_SIZE;
1027
1028
1029 long rootPageOffset = readLong( pageIos, dataPos );
1030 btreeHeader.setRootPageOffset( rootPageOffset );
1031 dataPos += LONG_SIZE;
1032
1033
1034 long btreeInfoOffset = readLong( pageIos, dataPos );
1035
1036
1037 PageIO[] infoPageIos = readPageIOs( btreeInfoOffset, Long.MAX_VALUE );
1038 ( ( PersistedBTree<K, V> ) btree ).setBtreeInfoOffset( infoPageIos[0].getOffset() );
1039 dataPos = 0L;
1040
1041
1042 int btreePageSize = readInt( infoPageIos, dataPos );
1043 BTreeFactory.setPageSize( btree, btreePageSize );
1044 dataPos += INT_SIZE;
1045
1046
1047 ByteBuffer btreeNameBytes = readBytes( infoPageIos, dataPos );
1048 dataPos += INT_SIZE + btreeNameBytes.limit();
1049 String btreeName = Strings.utf8ToString( btreeNameBytes );
1050 BTreeFactory.setName( btree, btreeName );
1051
1052
1053 ByteBuffer keySerializerBytes = readBytes( infoPageIos, dataPos );
1054 dataPos += INT_SIZE + keySerializerBytes.limit();
1055
1056 String keySerializerFqcn = "";
1057
1058 if ( keySerializerBytes != null )
1059 {
1060 keySerializerFqcn = Strings.utf8ToString( keySerializerBytes );
1061 }
1062
1063 BTreeFactory.setKeySerializer( btree, keySerializerFqcn );
1064
1065
1066 ByteBuffer valueSerializerBytes = readBytes( infoPageIos, dataPos );
1067
1068 String valueSerializerFqcn = "";
1069 dataPos += INT_SIZE + valueSerializerBytes.limit();
1070
1071 if ( valueSerializerBytes != null )
1072 {
1073 valueSerializerFqcn = Strings.utf8ToString( valueSerializerBytes );
1074 }
1075
1076 BTreeFactory.setValueSerializer( btree, valueSerializerFqcn );
1077
1078
1079 int allowDuplicates = readInt( infoPageIos, dataPos );
1080 ( ( PersistedBTree<K, V> ) btree ).setAllowDuplicates( allowDuplicates != 0 );
1081 dataPos += INT_SIZE;
1082
1083
1084 ( ( PersistedBTree<K, V> ) btree ).setRecordManager( this );
1085
1086
1087
1088
1089 ( ( PersistedBTree<K, V> ) btree ).storeRevision( btreeHeader, true );
1090
1091
1092 ( ( PersistedBTree<K, V> ) btree ).init( parentBTree );
1093
1094
1095 currentBTreeHeaders.put( btree.getName(), ( ( PersistedBTree<K, V> ) btree ).getBtreeHeader() );
1096 newBTreeHeaders.put( btree.getName(), ( ( PersistedBTree<K, V> ) btree ).getBtreeHeader() );
1097
1098
1099 PageIO[] rootPageIos = readPageIOs( rootPageOffset, Long.MAX_VALUE );
1100
1101 Page<K, V> btreeRoot = readPage( btree, rootPageIos );
1102 BTreeFactory.setRecordManager( btree, this );
1103
1104 BTreeFactory.setRootPage( btree, btreeRoot );
1105 }
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116 public <K, V> Page<K, V> deserialize( BTree<K, V> btree, long offset ) throws EndOfFileExceededException,
1117 IOException
1118 {
1119 checkOffset( offset );
1120 PageIO[] rootPageIos = readPageIOs( offset, Long.MAX_VALUE );
1121
1122 Page<K, V> page = readPage( btree, rootPageIos );
1123
1124 return page;
1125 }
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135 private <K, V> Page<K, V> readPage( BTree<K, V> btree, PageIO[] pageIos ) throws IOException
1136 {
1137
1138 long position = 0L;
1139
1140
1141 long revision = readLong( pageIos, position );
1142 position += LONG_SIZE;
1143
1144
1145 int nbElems = readInt( pageIos, position );
1146 position += INT_SIZE;
1147
1148
1149 Page<K, V> page = null;
1150
1151
1152
1153
1154 ByteBuffer byteBuffer = readBytes( pageIos, position );
1155
1156
1157
1158
1159 if ( nbElems >= 0 )
1160 {
1161
1162 page = readLeafKeysAndValues( btree, nbElems, revision, byteBuffer, pageIos );
1163 }
1164 else
1165 {
1166
1167 page = readNodeKeysAndValues( btree, -nbElems, revision, byteBuffer, pageIos );
1168 }
1169
1170 ( ( AbstractPage<K, V> ) page ).setOffset( pageIos[0].getOffset() );
1171 if ( pageIos.length > 1 )
1172 {
1173 ( ( AbstractPage<K, V> ) page ).setLastOffset( pageIos[pageIos.length - 1].getOffset() );
1174 }
1175
1176 return page;
1177 }
1178
1179
1180
1181
1182
1183 private <K, V> PersistedLeaf<K, V> readLeafKeysAndValues( BTree<K, V> btree, int nbElems, long revision,
1184 ByteBuffer byteBuffer, PageIO[] pageIos )
1185 {
1186
1187 PersistedLeaf<K, V> leaf = ( PersistedLeaf<K, V> ) BTreeFactory.createLeaf( btree, revision, nbElems );
1188
1189
1190 leaf.setOffset( pageIos[0].getOffset() );
1191 leaf.setLastOffset( pageIos[pageIos.length - 1].getOffset() );
1192
1193 int[] keyLengths = new int[nbElems];
1194 int[] valueLengths = new int[nbElems];
1195
1196 boolean isNotSubTree = ( btree.getType() != BTreeTypeEnum.PERSISTED_SUB );
1197
1198
1199 for ( int i = 0; i < nbElems; i++ )
1200 {
1201 if ( isNotSubTree )
1202 {
1203
1204 int nbValues = byteBuffer.getInt();
1205 PersistedValueHolder<V> valueHolder = null;
1206
1207 if ( nbValues < 0 )
1208 {
1209
1210 byte[] btreeOffsetBytes = new byte[LONG_SIZE];
1211 byteBuffer.get( btreeOffsetBytes );
1212
1213
1214
1215 valueHolder = new PersistedValueHolder<V>( btree, 1 - nbValues, btreeOffsetBytes );
1216 }
1217 else
1218 {
1219
1220
1221 valueLengths[i] = byteBuffer.getInt();
1222
1223
1224 byte[] arrayBytes = new byte[valueLengths[i]];
1225 byteBuffer.get( arrayBytes );
1226 valueHolder = new PersistedValueHolder<V>( btree, nbValues, arrayBytes );
1227 }
1228
1229 BTreeFactory.setValue( btree, leaf, i, valueHolder );
1230 }
1231
1232 keyLengths[i] = byteBuffer.getInt();
1233 byte[] data = new byte[keyLengths[i]];
1234 byteBuffer.get( data );
1235 BTreeFactory.setKey( btree, leaf, i, data );
1236 }
1237
1238 return leaf;
1239 }
1240
1241
1242
1243
1244
1245 private <K, V> PersistedNode<K, V> readNodeKeysAndValues( BTree<K, V> btree, int nbElems, long revision,
1246 ByteBuffer byteBuffer, PageIO[] pageIos ) throws IOException
1247 {
1248 PersistedNode<K, V> node = ( PersistedNode<K, V> ) BTreeFactory.createNode( btree, revision, nbElems );
1249
1250
1251 for ( int i = 0; i < nbElems; i++ )
1252 {
1253
1254 long offset = LongSerializer.INSTANCE.deserialize( byteBuffer );
1255 long lastOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
1256
1257 PersistedPageHolder<K, V> valueHolder = new PersistedPageHolder<K, V>( btree, null, offset, lastOffset );
1258 node.setValue( i, valueHolder );
1259
1260
1261 int keyLength = byteBuffer.getInt();
1262
1263 int currentPosition = byteBuffer.position();
1264
1265
1266 K key = btree.getKeySerializer().deserialize( byteBuffer );
1267
1268
1269 byteBuffer.position( currentPosition + keyLength );
1270
1271 BTreeFactory.setKey( btree, node, i, key );
1272 }
1273
1274
1275 long offset = LongSerializer.INSTANCE.deserialize( byteBuffer );
1276 long lastOffset = LongSerializer.INSTANCE.deserialize( byteBuffer );
1277
1278 PersistedPageHolder<K, V> valueHolder = new PersistedPageHolder<K, V>( btree, null, offset, lastOffset );
1279 node.setValue( nbElems, valueHolder );
1280
1281 return node;
1282 }
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292 ByteBuffer readBytes( PageIO[] pageIos, long position )
1293 {
1294
1295 int length = readInt( pageIos, position );
1296 position += INT_SIZE;
1297
1298
1299
1300 int pageNb = computePageNb( position );
1301
1302
1303 int pagePos = ( int ) ( position + ( pageNb + 1 ) * LONG_SIZE + INT_SIZE ) - pageNb * pageSize;
1304
1305
1306 int pageEnd = computePageNb( position + length );
1307
1308 if ( pageEnd > pageIos.length )
1309 {
1310
1311 LOG.error( "Wrong size : {}, it's larger than the number of provided pages {}", length, pageIos.length );
1312 throw new ArrayIndexOutOfBoundsException();
1313 }
1314
1315 ByteBuffer pageData = pageIos[pageNb].getData();
1316 int remaining = pageData.capacity() - pagePos;
1317
1318 if ( length == 0 )
1319 {
1320
1321 return null;
1322 }
1323 else
1324 {
1325 ByteBuffer bytes = ByteBuffer.allocate( length );
1326
1327 while ( length > 0 )
1328 {
1329 if ( length <= remaining )
1330 {
1331 pageData.mark();
1332 pageData.position( pagePos );
1333 int oldLimit = pageData.limit();
1334 pageData.limit( pagePos + length );
1335 bytes.put( pageData );
1336 pageData.limit( oldLimit );
1337 pageData.reset();
1338 bytes.rewind();
1339
1340 return bytes;
1341 }
1342
1343 pageData.mark();
1344 pageData.position( pagePos );
1345 int oldLimit = pageData.limit();
1346 pageData.limit( pagePos + remaining );
1347 bytes.put( pageData );
1348 pageData.limit( oldLimit );
1349 pageData.reset();
1350 pageNb++;
1351 pagePos = LINK_SIZE;
1352 pageData = pageIos[pageNb].getData();
1353 length -= remaining;
1354 remaining = pageData.capacity() - pagePos;
1355 }
1356
1357 bytes.rewind();
1358
1359 return bytes;
1360 }
1361 }
1362
1363
1364
1365
1366
1367
1368
1369
1370 int readInt( PageIO[] pageIos, long position )
1371 {
1372
1373
1374 int pageNb = computePageNb( position );
1375
1376
1377 int pagePos = ( int ) ( position + ( pageNb + 1 ) * LONG_SIZE + INT_SIZE ) - pageNb * pageSize;
1378
1379 ByteBuffer pageData = pageIos[pageNb].getData();
1380 int remaining = pageData.capacity() - pagePos;
1381 int value = 0;
1382
1383 if ( remaining >= INT_SIZE )
1384 {
1385 value = pageData.getInt( pagePos );
1386 }
1387 else
1388 {
1389 value = 0;
1390
1391 switch ( remaining )
1392 {
1393 case 3:
1394 value += ( ( pageData.get( pagePos + 2 ) & 0x00FF ) << 8 );
1395
1396
1397 case 2:
1398 value += ( ( pageData.get( pagePos + 1 ) & 0x00FF ) << 16 );
1399
1400
1401 case 1:
1402 value += ( pageData.get( pagePos ) << 24 );
1403 break;
1404 }
1405
1406
1407 pageData = pageIos[pageNb + 1].getData();
1408 pagePos = LINK_SIZE;
1409
1410 switch ( remaining )
1411 {
1412 case 1:
1413 value += ( pageData.get( pagePos ) & 0x00FF ) << 16;
1414
1415
1416 case 2:
1417 value += ( pageData.get( pagePos + 2 - remaining ) & 0x00FF ) << 8;
1418
1419
1420 case 3:
1421 value += ( pageData.get( pagePos + 3 - remaining ) & 0x00FF );
1422 break;
1423 }
1424 }
1425
1426 return value;
1427 }
1428
1429
1430
1431
1432
1433
1434
1435
1436 private byte readByte( PageIO[] pageIos, long position )
1437 {
1438
1439
1440 int pageNb = computePageNb( position );
1441
1442
1443 int pagePos = ( int ) ( position + ( pageNb + 1 ) * LONG_SIZE + INT_SIZE ) - pageNb * pageSize;
1444
1445 ByteBuffer pageData = pageIos[pageNb].getData();
1446 byte value = 0;
1447
1448 value = pageData.get( pagePos );
1449
1450 return value;
1451 }
1452
1453
1454
1455
1456
1457
1458
1459
1460 long readLong( PageIO[] pageIos, long position )
1461 {
1462
1463
1464 int pageNb = computePageNb( position );
1465
1466
1467 int pagePos = ( int ) ( position + ( pageNb + 1 ) * LONG_SIZE + INT_SIZE ) - pageNb * pageSize;
1468
1469 ByteBuffer pageData = pageIos[pageNb].getData();
1470 int remaining = pageData.capacity() - pagePos;
1471 long value = 0L;
1472
1473 if ( remaining >= LONG_SIZE )
1474 {
1475 value = pageData.getLong( pagePos );
1476 }
1477 else
1478 {
1479 switch ( remaining )
1480 {
1481 case 7:
1482 value += ( ( ( long ) pageData.get( pagePos + 6 ) & 0x00FF ) << 8 );
1483
1484
1485 case 6:
1486 value += ( ( ( long ) pageData.get( pagePos + 5 ) & 0x00FF ) << 16 );
1487
1488
1489 case 5:
1490 value += ( ( ( long ) pageData.get( pagePos + 4 ) & 0x00FF ) << 24 );
1491
1492
1493 case 4:
1494 value += ( ( ( long ) pageData.get( pagePos + 3 ) & 0x00FF ) << 32 );
1495
1496
1497 case 3:
1498 value += ( ( ( long ) pageData.get( pagePos + 2 ) & 0x00FF ) << 40 );
1499
1500
1501 case 2:
1502 value += ( ( ( long ) pageData.get( pagePos + 1 ) & 0x00FF ) << 48 );
1503
1504
1505 case 1:
1506 value += ( ( long ) pageData.get( pagePos ) << 56 );
1507 break;
1508 }
1509
1510
1511 pageData = pageIos[pageNb + 1].getData();
1512 pagePos = LINK_SIZE;
1513
1514 switch ( remaining )
1515 {
1516 case 1:
1517 value += ( ( long ) pageData.get( pagePos ) & 0x00FF ) << 48;
1518
1519
1520 case 2:
1521 value += ( ( long ) pageData.get( pagePos + 2 - remaining ) & 0x00FF ) << 40;
1522
1523
1524 case 3:
1525 value += ( ( long ) pageData.get( pagePos + 3 - remaining ) & 0x00FF ) << 32;
1526
1527
1528 case 4:
1529 value += ( ( long ) pageData.get( pagePos + 4 - remaining ) & 0x00FF ) << 24;
1530
1531
1532 case 5:
1533 value += ( ( long ) pageData.get( pagePos + 5 - remaining ) & 0x00FF ) << 16;
1534
1535
1536 case 6:
1537 value += ( ( long ) pageData.get( pagePos + 6 - remaining ) & 0x00FF ) << 8;
1538
1539
1540 case 7:
1541 value += ( ( long ) pageData.get( pagePos + 7 - remaining ) & 0x00FF );
1542 break;
1543 }
1544 }
1545
1546 return value;
1547 }
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564 public synchronized <K, V> void manage( BTree<K, V> btree ) throws BTreeAlreadyManagedException, IOException
1565 {
1566 beginTransaction();
1567
1568 try
1569 {
1570 LOG.debug( "Managing the btree {}", btree.getName() );
1571 BTreeFactory.setRecordManager( btree, this );
1572
1573 String name = btree.getName();
1574
1575 if ( managedBtrees.containsKey( name ) )
1576 {
1577
1578 LOG.error( "There is already a B-tree named '{}' managed by this recordManager", name );
1579 rollback();
1580 throw new BTreeAlreadyManagedException( name );
1581 }
1582
1583
1584 long btreeInfoOffset = writeBtreeInfo( btree );
1585 BTreeHeader<K, V> btreeHeader = ( ( AbstractBTree<K, V> ) btree ).getBtreeHeader();
1586 ( ( PersistedBTree<K, V> ) btree ).setBtreeInfoOffset( btreeInfoOffset );
1587
1588
1589 Page<K, V> rootPage = btreeHeader.getRootPage();
1590
1591 PageIO[] rootPageIos = serializePage( btree, btreeHeader.getRevision(), rootPage );
1592
1593
1594 long rootPageOffset = rootPageIos[0].getOffset();
1595
1596
1597 btreeHeader.setRootPageOffset( rootPageOffset );
1598 ( ( PersistedLeaf<K, V> ) rootPage ).setOffset( rootPageOffset );
1599
1600 LOG.debug( "Flushing the newly managed '{}' btree rootpage", btree.getName() );
1601 flushPages( rootPageIos );
1602
1603
1604 long btreeHeaderOffset = writeBtreeHeader( btree, btreeHeader );
1605
1606
1607
1608 managedBtrees.put( name, ( BTree<Object, Object> ) btree );
1609
1610
1611 currentBTreeHeaders.put( name, btreeHeader );
1612 newBTreeHeaders.put( name, btreeHeader );
1613
1614
1615 nbBtree++;
1616
1617
1618 NameRevision nameRevision = new NameRevision( name, 0L );
1619
1620
1621 btreeOfBtrees.insert( nameRevision, btreeHeaderOffset );
1622 commit();
1623 }
1624 catch ( IOException ioe )
1625 {
1626 rollback();
1627 throw ioe;
1628 }
1629 }
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643 public synchronized <K, V> void manageSubBtree( BTree<K, V> btree )
1644 throws BTreeAlreadyManagedException, IOException
1645 {
1646 LOG.debug( "Managing the sub-btree {}", btree.getName() );
1647 BTreeFactory.setRecordManager( btree, this );
1648
1649 String name = btree.getName();
1650
1651 if ( managedBtrees.containsKey( name ) )
1652 {
1653
1654 LOG.error( "There is already a sub-B-tree named '{}' managed by this recordManager", name );
1655 throw new BTreeAlreadyManagedException( name );
1656 }
1657
1658
1659 long btreeInfoOffset = writeBtreeInfo( btree );
1660 BTreeHeader<K, V> btreeHeader = ( ( AbstractBTree<K, V> ) btree ).getBtreeHeader();
1661 ( ( PersistedBTree<K, V> ) btree ).setBtreeInfoOffset( btreeInfoOffset );
1662
1663
1664 Page<K, V> rootPage = btreeHeader.getRootPage();
1665
1666 PageIO[] rootPageIos = serializePage( btree, btreeHeader.getRevision(), rootPage );
1667
1668
1669 long rootPageOffset = rootPageIos[0].getOffset();
1670
1671
1672 btreeHeader.setRootPageOffset( rootPageOffset );
1673
1674 ( ( AbstractPage<K, V> ) rootPage ).setOffset( rootPageOffset );
1675
1676 LOG.debug( "Flushing the newly managed '{}' btree rootpage", btree.getName() );
1677 flushPages( rootPageIos );
1678
1679
1680 long btreeHeaderOffset = writeBtreeHeader( btree, btreeHeader );
1681
1682
1683
1684 if ( ( btree.getType() != BTreeTypeEnum.BTREE_OF_BTREES ) &&
1685 ( btree.getType() != BTreeTypeEnum.COPIED_PAGES_BTREE ) &&
1686 ( btree.getType() != BTreeTypeEnum.PERSISTED_SUB ) )
1687 {
1688 managedBtrees.put( name, ( BTree<Object, Object> ) btree );
1689 }
1690
1691
1692 currentBTreeHeaders.put( name, btreeHeader );
1693 newBTreeHeaders.put( name, btreeHeader );
1694
1695
1696 NameRevision nameRevision = new NameRevision( name, 0L );
1697
1698
1699 if ( ( btree.getType() != BTreeTypeEnum.BTREE_OF_BTREES ) &&
1700 ( btree.getType() != BTreeTypeEnum.COPIED_PAGES_BTREE ) &&
1701 ( btree.getType() != BTreeTypeEnum.PERSISTED_SUB ) )
1702 {
1703
1704 nbBtree++;
1705
1706 btreeOfBtrees.insert( nameRevision, btreeHeaderOffset );
1707 }
1708
1709 updateRecordManagerHeader();
1710 }
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731 private <K, V> PageIO[] serializePage( BTree<K, V> btree, long revision, Page<K, V> page ) throws IOException
1732 {
1733 int nbElems = page.getNbElems();
1734
1735 boolean isNotSubTree = ( btree.getType() != BTreeTypeEnum.PERSISTED_SUB );
1736
1737 if ( nbElems == 0 )
1738 {
1739 return serializeRootPage( revision );
1740 }
1741 else
1742 {
1743
1744 int nbBuffers = 1 + 1 + 1 + nbElems * 3;
1745 int dataSize = 0;
1746 int serializedSize = 0;
1747
1748 if ( page.isNode() )
1749 {
1750
1751 nbBuffers++;
1752 }
1753
1754
1755 List<byte[]> serializedData = new ArrayList<byte[]>( nbBuffers );
1756
1757
1758 byte[] buffer = LongSerializer.serialize( revision );
1759 serializedData.add( buffer );
1760 serializedSize += buffer.length;
1761
1762
1763
1764 int pageNbElems = nbElems;
1765
1766 if ( page.isNode() )
1767 {
1768 pageNbElems = -nbElems;
1769 }
1770
1771 buffer = IntSerializer.serialize( pageNbElems );
1772 serializedData.add( buffer );
1773 serializedSize += buffer.length;
1774
1775
1776
1777
1778 for ( int pos = 0; pos < nbElems; pos++ )
1779 {
1780
1781 if ( page.isNode() )
1782 {
1783 dataSize += serializeNodeValue( ( PersistedNode<K, V> ) page, pos, serializedData );
1784 dataSize += serializeNodeKey( ( PersistedNode<K, V> ) page, pos, serializedData );
1785 }
1786 else
1787 {
1788 if ( isNotSubTree )
1789 {
1790 dataSize += serializeLeafValue( ( PersistedLeaf<K, V> ) page, pos, serializedData );
1791 }
1792
1793 dataSize += serializeLeafKey( ( PersistedLeaf<K, V> ) page, pos, serializedData );
1794 }
1795 }
1796
1797
1798 if ( page.isNode() )
1799 {
1800 dataSize += serializeNodeValue( ( PersistedNode<K, V> ) page, nbElems, serializedData );
1801 }
1802
1803
1804 buffer = IntSerializer.serialize( dataSize );
1805 serializedData.add( 2, buffer );
1806 serializedSize += buffer.length;
1807
1808 serializedSize += dataSize;
1809
1810
1811 PageIO[] pageIos = getFreePageIOs( serializedSize );
1812
1813
1814 long position = 0L;
1815
1816 for ( byte[] bytes : serializedData )
1817 {
1818 position = storeRaw( position, bytes, pageIos );
1819 }
1820
1821 return pageIos;
1822 }
1823 }
1824
1825
1826
1827
1828
1829 private <K, V> int serializeNodeKey( PersistedNode<K, V> node, int pos, List<byte[]> serializedData )
1830 {
1831 KeyHolder<K> holder = node.getKeyHolder( pos );
1832 byte[] buffer = ( ( PersistedKeyHolder<K> ) holder ).getRaw();
1833
1834
1835 byte[] length = IntSerializer.serialize( buffer.length );
1836 serializedData.add( length );
1837
1838
1839 if ( buffer.length != 0 )
1840 {
1841 serializedData.add( buffer );
1842 }
1843
1844 return buffer.length + INT_SIZE;
1845 }
1846
1847
1848
1849
1850
1851 private <K, V> int serializeNodeValue( PersistedNode<K, V> node, int pos, List<byte[]> serializedData )
1852 throws IOException
1853 {
1854
1855 Page<K, V> child = node.getReference( pos );
1856
1857
1858 byte[] buffer = LongSerializer.serialize( ( ( AbstractPage<K, V> ) child ).getOffset() );
1859 serializedData.add( buffer );
1860 int dataSize = buffer.length;
1861
1862
1863 buffer = LongSerializer.serialize( ( ( AbstractPage<K, V> ) child ).getLastOffset() );
1864 serializedData.add( buffer );
1865 dataSize += buffer.length;
1866
1867 return dataSize;
1868 }
1869
1870
1871
1872
1873
1874 private <K, V> int serializeLeafKey( PersistedLeaf<K, V> leaf, int pos, List<byte[]> serializedData )
1875 {
1876 int dataSize = 0;
1877 KeyHolder<K> keyHolder = leaf.getKeyHolder( pos );
1878 byte[] keyData = ( ( PersistedKeyHolder<K> ) keyHolder ).getRaw();
1879
1880 if ( keyData != null )
1881 {
1882
1883 byte[] length = IntSerializer.serialize( keyData.length );
1884 serializedData.add( length );
1885
1886
1887 serializedData.add( keyData );
1888 dataSize += keyData.length + INT_SIZE;
1889 }
1890 else
1891 {
1892 serializedData.add( IntSerializer.serialize( 0 ) );
1893 dataSize += INT_SIZE;
1894 }
1895
1896 return dataSize;
1897 }
1898
1899
1900
1901
1902
1903 private <K, V> int serializeLeafValue( PersistedLeaf<K, V> leaf, int pos, List<byte[]> serializedData )
1904 throws IOException
1905 {
1906
1907
1908 ValueHolder<V> valueHolder = leaf.getValue( pos );
1909 int dataSize = 0;
1910 int nbValues = valueHolder.size();
1911
1912 if ( nbValues == 0 )
1913 {
1914
1915 byte[] buffer = IntSerializer.serialize( nbValues );
1916 serializedData.add( buffer );
1917
1918 return buffer.length;
1919 }
1920
1921 if ( !valueHolder.isSubBtree() )
1922 {
1923
1924 byte[] buffer = IntSerializer.serialize( nbValues );
1925 serializedData.add( buffer );
1926 dataSize = INT_SIZE;
1927
1928
1929 byte[] data = ( ( PersistedValueHolder<V> ) valueHolder ).getRaw();
1930 dataSize += data.length;
1931
1932
1933 buffer = IntSerializer.serialize( data.length );
1934 serializedData.add( buffer );
1935 dataSize += INT_SIZE;
1936
1937
1938 if ( data.length > 0 )
1939 {
1940 serializedData.add( data );
1941 }
1942 }
1943 else
1944 {
1945
1946 byte[] buffer = IntSerializer.serialize( -( nbValues + 1 ) );
1947 serializedData.add( buffer );
1948 dataSize += buffer.length;
1949
1950
1951 buffer = LongSerializer.serialize( ( ( PersistedValueHolder<V> ) valueHolder ).getOffset() );
1952 serializedData.add( buffer );
1953 dataSize += buffer.length;
1954 }
1955
1956 return dataSize;
1957 }
1958
1959
1960
1961
1962
1963 private PageIO[] serializeRootPage( long revision ) throws IOException
1964 {
1965
1966 PageIO[] pageIos = new PageIO[1];
1967
1968
1969 PageIO newPage = fetchNewPage();
1970
1971
1972
1973
1974 long position = 0L;
1975
1976 position = store( position, revision, newPage );
1977 position = store( position, 0, newPage );
1978
1979
1980 newPage.setSize( ( int ) position );
1981
1982
1983 pageIos[0] = newPage;
1984
1985 return pageIos;
1986 }
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010 public void updateRecordManagerHeader()
2011 {
2012
2013 int position = writeData( RECORD_MANAGER_HEADER_BYTES, 0, pageSize );
2014
2015
2016 position = writeData( RECORD_MANAGER_HEADER_BYTES, position, nbBtree );
2017
2018
2019 position = writeData( RECORD_MANAGER_HEADER_BYTES, position, firstFreePage );
2020
2021
2022 position = writeData( RECORD_MANAGER_HEADER_BYTES, position, currentBtreeOfBtreesOffset );
2023
2024
2025 position = writeData( RECORD_MANAGER_HEADER_BYTES, position, previousBtreeOfBtreesOffset );
2026
2027
2028 position = writeData( RECORD_MANAGER_HEADER_BYTES, position, currentCopiedPagesBtreeOffset );
2029
2030
2031 position = writeData( RECORD_MANAGER_HEADER_BYTES, position, previousCopiedPagesBtreeOffset );
2032
2033
2034 RECORD_MANAGER_HEADER_BUFFER.put( RECORD_MANAGER_HEADER_BYTES );
2035 RECORD_MANAGER_HEADER_BUFFER.flip();
2036
2037 LOG.debug( "Update RM header" );
2038
2039 if ( LOG_PAGES.isDebugEnabled() )
2040 {
2041 StringBuilder sb = new StringBuilder();
2042
2043 sb.append( "First free page : 0x" ).append( Long.toHexString( firstFreePage ) ).append( "\n" );
2044 sb.append( "Current BOB header : 0x" ).append( Long.toHexString( currentBtreeOfBtreesOffset ) ).append( "\n" );
2045 sb.append( "Previous BOB header : 0x" ).append( Long.toHexString( previousBtreeOfBtreesOffset ) ).append( "\n" );
2046 sb.append( "Current CPB header : 0x" ).append( Long.toHexString( currentCopiedPagesBtreeOffset ) ).append( "\n" );
2047 sb.append( "Previous CPB header : 0x" ).append( Long.toHexString( previousCopiedPagesBtreeOffset ) ).append( "\n" );
2048
2049 if ( firstFreePage != NO_PAGE )
2050 {
2051 long freePage = firstFreePage;
2052 sb.append( "free pages list : " );
2053
2054 boolean isFirst = true;
2055
2056 while ( freePage != NO_PAGE )
2057 {
2058 if ( isFirst )
2059 {
2060 isFirst = false;
2061 }
2062 else
2063 {
2064 sb.append( " -> " );
2065 }
2066
2067 sb.append( "0x" ).append( Long.toHexString( freePage ) );
2068
2069 try
2070 {
2071 PageIO[] freePageIO = readPageIOs( freePage, 8 );
2072
2073 freePage = freePageIO[0].getNextPage();
2074 }
2075 catch ( EndOfFileExceededException e )
2076 {
2077
2078 e.printStackTrace();
2079 }
2080 catch ( IOException e )
2081 {
2082
2083 e.printStackTrace();
2084 }
2085 }
2086
2087 }
2088
2089 LOG_PAGES.debug( "Update RM Header : \n{}", sb.toString() );
2090 }
2091
2092 try
2093 {
2094
2095 Integer nbTxnStarted = context.get();
2096
2097 if ( ( nbTxnStarted == null ) || ( nbTxnStarted <= 1 ) )
2098 {
2099
2100 writeCounter.put( 0L, writeCounter.containsKey( 0L ) ? writeCounter.get( 0L ) + 1 : 1 );
2101 fileChannel.write( RECORD_MANAGER_HEADER_BUFFER, 0 );
2102 }
2103 }
2104 catch ( IOException ioe )
2105 {
2106 throw new FileException( ioe.getMessage() );
2107 }
2108
2109 RECORD_MANAGER_HEADER_BUFFER.clear();
2110
2111
2112 previousBtreeOfBtreesOffset = -1L;
2113 previousCopiedPagesBtreeOffset = -1L;
2114
2115 nbUpdateRMHeader.incrementAndGet();
2116 }
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140 public void updateRecordManagerHeader( long newBtreeOfBtreesOffset, long newCopiedPageBtreeOffset )
2141 {
2142 if ( newBtreeOfBtreesOffset != -1L )
2143 {
2144 previousBtreeOfBtreesOffset = currentBtreeOfBtreesOffset;
2145 currentBtreeOfBtreesOffset = newBtreeOfBtreesOffset;
2146 }
2147
2148 if ( newCopiedPageBtreeOffset != -1L )
2149 {
2150 previousCopiedPagesBtreeOffset = currentCopiedPagesBtreeOffset;
2151 currentCopiedPagesBtreeOffset = newCopiedPageBtreeOffset;
2152 }
2153 }
2154
2155
2156
2157
2158
2159 private int writeData( byte[] buffer, int position, int value )
2160 {
2161 RECORD_MANAGER_HEADER_BYTES[position] = ( byte ) ( value >>> 24 );
2162 RECORD_MANAGER_HEADER_BYTES[position + 1] = ( byte ) ( value >>> 16 );
2163 RECORD_MANAGER_HEADER_BYTES[position + 2] = ( byte ) ( value >>> 8 );
2164 RECORD_MANAGER_HEADER_BYTES[position + 3] = ( byte ) ( value );
2165
2166 return position + 4;
2167 }
2168
2169
2170
2171
2172
2173 private int writeData( byte[] buffer, int position, long value )
2174 {
2175 RECORD_MANAGER_HEADER_BYTES[position] = ( byte ) ( value >>> 56 );
2176 RECORD_MANAGER_HEADER_BYTES[position + 1] = ( byte ) ( value >>> 48 );
2177 RECORD_MANAGER_HEADER_BYTES[position + 2] = ( byte ) ( value >>> 40 );
2178 RECORD_MANAGER_HEADER_BYTES[position + 3] = ( byte ) ( value >>> 32 );
2179 RECORD_MANAGER_HEADER_BYTES[position + 4] = ( byte ) ( value >>> 24 );
2180 RECORD_MANAGER_HEADER_BYTES[position + 5] = ( byte ) ( value >>> 16 );
2181 RECORD_MANAGER_HEADER_BYTES[position + 6] = ( byte ) ( value >>> 8 );
2182 RECORD_MANAGER_HEADER_BYTES[position + 7] = ( byte ) ( value );
2183
2184 return position + 8;
2185 }
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196 <K, V> void addInBtreeOfBtrees( String name, long revision, long btreeHeaderOffset )
2197 throws IOException
2198 {
2199 checkOffset( btreeHeaderOffset );
2200 NameRevision nameRevision = new NameRevision( name, revision );
2201
2202 btreeOfBtrees.insert( nameRevision, btreeHeaderOffset );
2203
2204
2205 currentBtreeOfBtreesOffset = getNewBTreeHeader( BTREE_OF_BTREES_NAME ).getBTreeHeaderOffset();
2206 }
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217 <K, V> void addInCopiedPagesBtree( String name, long revision, List<Page<K, V>> pages )
2218 throws IOException
2219 {
2220 RevisionName revisionName = new RevisionName( revision, name );
2221
2222 long[] pageOffsets = new long[pages.size()];
2223 int pos = 0;
2224
2225 for ( Page<K, V> page : pages )
2226 {
2227 pageOffsets[pos++] = ( ( AbstractPage<K, V> ) page ).getOffset();
2228 }
2229
2230 copiedPageBtree.insert( revisionName, pageOffsets );
2231
2232
2233 currentCopiedPagesBtreeOffset = ( ( AbstractBTree<RevisionName, long[]> ) copiedPageBtree ).getBtreeHeader().getBTreeHeaderOffset();
2234 }
2235
2236
2237
2238
2239
2240
2241 void setBtreeOfBtreesOffset( long btreeOfBtreesOffset )
2242 {
2243 checkOffset( btreeOfBtreesOffset );
2244 this.currentBtreeOfBtreesOffset = btreeOfBtreesOffset;
2245 }
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266 <K, V> long writeBtreeHeader( BTree<K, V> btree, BTreeHeader<K, V> btreeHeader )
2267 throws IOException
2268 {
2269 int bufferSize =
2270 LONG_SIZE +
2271 LONG_SIZE +
2272 LONG_SIZE +
2273 LONG_SIZE;
2274
2275
2276 PageIO[] btreeHeaderPageIos = getFreePageIOs( bufferSize );
2277
2278
2279 long btreeHeaderOffset = btreeHeaderPageIos[0].getOffset();
2280
2281
2282
2283
2284
2285
2286
2287 long position = 0L;
2288
2289
2290 position = store( position, btreeHeader.getRevision(), btreeHeaderPageIos );
2291
2292
2293 position = store( position, btreeHeader.getNbElems(), btreeHeaderPageIos );
2294
2295
2296 position = store( position, btreeHeader.getRootPageOffset(), btreeHeaderPageIos );
2297
2298
2299 position = store( position, ( ( PersistedBTree<K, V> ) btree ).getBtreeInfoOffset(), btreeHeaderPageIos );
2300
2301
2302 LOG.debug( "Flushing the newly managed '{}' btree header", btree.getName() );
2303
2304 if ( LOG_PAGES.isDebugEnabled() )
2305 {
2306 LOG_PAGES.debug( "Writing BTreeHeader revision {} for {}", btreeHeader.getRevision(), btree.getName() );
2307 StringBuilder sb = new StringBuilder();
2308
2309 sb.append( "Offset : " ).append( Long.toHexString( btreeHeaderOffset ) ).append( "\n" );
2310 sb.append( " Revision : " ).append( btreeHeader.getRevision() ).append( "\n" );
2311 sb.append( " NbElems : " ).append( btreeHeader.getNbElems() ).append( "\n" );
2312 sb.append( " RootPage : 0x" ).append( Long.toHexString( btreeHeader.getRootPageOffset() ) )
2313 .append( "\n" );
2314 sb.append( " Info : 0x" )
2315 .append( Long.toHexString( ( ( PersistedBTree<K, V> ) btree ).getBtreeInfoOffset() ) ).append( "\n" );
2316
2317 LOG_PAGES.debug( "Btree Header[{}]\n{}", btreeHeader.getRevision(), sb.toString() );
2318 }
2319
2320 flushPages( btreeHeaderPageIos );
2321
2322 btreeHeader.setBTreeHeaderOffset( btreeHeaderOffset );
2323
2324 return btreeHeaderOffset;
2325 }
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353 private <K, V> long writeBtreeInfo( BTree<K, V> btree ) throws IOException
2354 {
2355
2356 byte[] btreeNameBytes = Strings.getBytesUtf8( btree.getName() );
2357 byte[] keySerializerBytes = Strings.getBytesUtf8( btree.getKeySerializerFQCN() );
2358 byte[] valueSerializerBytes = Strings.getBytesUtf8( btree.getValueSerializerFQCN() );
2359
2360 int bufferSize =
2361 INT_SIZE +
2362 INT_SIZE +
2363 btreeNameBytes.length +
2364 INT_SIZE +
2365 keySerializerBytes.length +
2366 INT_SIZE +
2367 valueSerializerBytes.length +
2368 INT_SIZE;
2369
2370
2371 PageIO[] btreeHeaderPageIos = getFreePageIOs( bufferSize );
2372
2373
2374 long btreeInfoOffset = btreeHeaderPageIos[0].getOffset();
2375
2376
2377
2378
2379
2380
2381
2382
2383 long position = 0L;
2384
2385
2386 position = store( position, btree.getPageSize(), btreeHeaderPageIos );
2387
2388
2389 position = store( position, btreeNameBytes, btreeHeaderPageIos );
2390
2391
2392 position = store( position, keySerializerBytes, btreeHeaderPageIos );
2393
2394
2395 position = store( position, valueSerializerBytes, btreeHeaderPageIos );
2396
2397
2398 position = store( position, ( btree.isAllowDuplicates() ? 1 : 0 ), btreeHeaderPageIos );
2399
2400
2401 LOG.debug( "Flushing the newly managed '{}' btree header", btree.getName() );
2402 flushPages( btreeHeaderPageIos );
2403
2404 return btreeInfoOffset;
2405 }
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426 <K, V> long updateBtreeHeader( BTree<K, V> btree, long btreeHeaderOffset )
2427 throws EndOfFileExceededException, IOException
2428 {
2429 return updateBtreeHeader( btree, btreeHeaderOffset, false );
2430 }
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451 <K, V> void updateBtreeHeaderOnPlace( BTree<K, V> btree, long btreeHeaderOffset )
2452 throws EndOfFileExceededException,
2453 IOException
2454 {
2455 updateBtreeHeader( btree, btreeHeaderOffset, true );
2456 }
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481 private <K, V> long updateBtreeHeader( BTree<K, V> btree, long btreeHeaderOffset, boolean onPlace )
2482 throws EndOfFileExceededException, IOException
2483 {
2484
2485 PageIO[] pageIos;
2486 long newBtreeHeaderOffset = NO_PAGE;
2487 long offset = ( ( PersistedBTree<K, V> ) btree ).getBtreeOffset();
2488
2489 if ( onPlace )
2490 {
2491
2492 long headerSize = LONG_SIZE + LONG_SIZE + LONG_SIZE;
2493
2494 pageIos = readPageIOs( offset, headerSize );
2495
2496
2497 long position = 0;
2498
2499 position = store( position, btree.getRevision(), pageIos );
2500 position = store( position, btree.getNbElems(), pageIos );
2501 position = store( position, btreeHeaderOffset, pageIos );
2502
2503
2504 if ( LOG.isDebugEnabled() )
2505 {
2506 LOG.debug( "-----> Flushing the '{}' B-treeHeader", btree.getName() );
2507 LOG.debug( " revision : " + btree.getRevision() + ", NbElems : " + btree.getNbElems()
2508 + ", btreeHeader offset : 0x"
2509 + Long.toHexString( btreeHeaderOffset ) );
2510 }
2511
2512
2513
2514 LOG.debug( "Rewriting the B-treeHeader on place for B-tree " + btree.getName() );
2515 flushPages( pageIos );
2516 }
2517 else
2518 {
2519
2520 pageIos = readPageIOs( offset, Long.MAX_VALUE );
2521
2522
2523 PageIO[] newPageIOs = new PageIO[pageIos.length];
2524 int pos = 0;
2525
2526 for ( PageIO pageIo : pageIos )
2527 {
2528
2529 newPageIOs[pos] = fetchNewPage();
2530
2531
2532
2533 if ( ( btree.getType() == BTreeTypeEnum.BTREE_OF_BTREES )
2534 || ( btree.getType() == BTreeTypeEnum.COPIED_PAGES_BTREE ) )
2535 {
2536 freedPages.add( pageIo );
2537 allocatedPages.add( newPageIOs[pos] );
2538 }
2539
2540 pageIo.copy( newPageIOs[pos] );
2541
2542 if ( pos > 0 )
2543 {
2544 newPageIOs[pos - 1].setNextPage( newPageIOs[pos].getOffset() );
2545 }
2546
2547 pos++;
2548 }
2549
2550
2551
2552 long position = 0;
2553
2554 position = store( position, btree.getRevision(), newPageIOs );
2555 position = store( position, btree.getNbElems(), newPageIOs );
2556 position = store( position, btreeHeaderOffset, newPageIOs );
2557
2558
2559
2560 LOG.debug( "Rewriting the B-treeHeader on place for B-tree " + btree.getName() );
2561 flushPages( newPageIOs );
2562
2563 newBtreeHeaderOffset = newPageIOs[0].getOffset();
2564 }
2565
2566 nbUpdateBtreeHeader.incrementAndGet();
2567
2568 if ( LOG_CHECK.isDebugEnabled() )
2569 {
2570 MavibotInspector.check( this );
2571 }
2572
2573 return newBtreeHeaderOffset;
2574 }
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584 private void flushPages( PageIO... pageIos ) throws IOException
2585 {
2586 if ( LOG.isDebugEnabled() )
2587 {
2588 for ( PageIO pageIo : pageIos )
2589 {
2590 dump( pageIo );
2591 }
2592 }
2593
2594 for ( PageIO pageIo : pageIos )
2595 {
2596 pageIo.getData().rewind();
2597 long pos = pageIo.getOffset();
2598
2599 if ( fileChannel.size() < ( pageIo.getOffset() + pageSize ) )
2600 {
2601 LOG.debug( "Adding a page at the end of the file" );
2602
2603 pos = fileChannel.size();
2604 fileChannel.write( pageIo.getData(), pos );
2605
2606 }
2607 else
2608 {
2609 LOG.debug( "Writing a page at position {}", pageIo.getOffset() );
2610 fileChannel.write( pageIo.getData(), pageIo.getOffset() );
2611
2612 }
2613
2614
2615 writeCounter.put( pos, writeCounter.containsKey( pos ) ? writeCounter.get( pos ) + 1 : 1 );
2616
2617 nbUpdatePageIOs.incrementAndGet();
2618
2619 pageIo.getData().rewind();
2620 }
2621 }
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631 private int computePageNb( long offset )
2632 {
2633 long pageNb = 0;
2634
2635 offset -= pageSize - LINK_SIZE - PAGE_SIZE;
2636
2637 if ( offset < 0 )
2638 {
2639 return ( int ) pageNb;
2640 }
2641
2642 pageNb = 1 + offset / ( pageSize - LINK_SIZE );
2643
2644 return ( int ) pageNb;
2645 }
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657 private long store( long position, byte[] bytes, PageIO... pageIos )
2658 {
2659 if ( bytes != null )
2660 {
2661
2662 position = store( position, bytes.length, pageIos );
2663
2664
2665
2666 int pageNb = computePageNb( position );
2667
2668
2669 ByteBuffer pageData = pageIos[pageNb].getData();
2670
2671
2672 int pagePos = ( int ) ( position + ( pageNb + 1 ) * LONG_SIZE + INT_SIZE ) - pageNb * pageSize;
2673
2674
2675 int remaining = pageData.capacity() - pagePos;
2676 int nbStored = bytes.length;
2677
2678
2679 while ( nbStored > 0 )
2680 {
2681 if ( remaining > nbStored )
2682 {
2683 pageData.mark();
2684 pageData.position( pagePos );
2685 pageData.put( bytes, bytes.length - nbStored, nbStored );
2686 pageData.reset();
2687 nbStored = 0;
2688 }
2689 else
2690 {
2691 pageData.mark();
2692 pageData.position( pagePos );
2693 pageData.put( bytes, bytes.length - nbStored, remaining );
2694 pageData.reset();
2695 pageNb++;
2696 pageData = pageIos[pageNb].getData();
2697 pagePos = LINK_SIZE;
2698 nbStored -= remaining;
2699 remaining = pageData.capacity() - pagePos;
2700 }
2701 }
2702
2703
2704 position += bytes.length;
2705 }
2706 else
2707 {
2708
2709 position = store( position, 0, pageIos );
2710 }
2711
2712 return position;
2713 }
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726 private long storeRaw( long position, byte[] bytes, PageIO... pageIos )
2727 {
2728 if ( bytes != null )
2729 {
2730
2731
2732 int pageNb = computePageNb( position );
2733
2734
2735 ByteBuffer pageData = pageIos[pageNb].getData();
2736
2737
2738 int pagePos = ( int ) ( position + ( pageNb + 1 ) * LONG_SIZE + INT_SIZE ) - pageNb * pageSize;
2739
2740
2741 int remaining = pageData.capacity() - pagePos;
2742 int nbStored = bytes.length;
2743
2744
2745 while ( nbStored > 0 )
2746 {
2747 if ( remaining > nbStored )
2748 {
2749 pageData.mark();
2750 pageData.position( pagePos );
2751 pageData.put( bytes, bytes.length - nbStored, nbStored );
2752 pageData.reset();
2753 nbStored = 0;
2754 }
2755 else
2756 {
2757 pageData.mark();
2758 pageData.position( pagePos );
2759 pageData.put( bytes, bytes.length - nbStored, remaining );
2760 pageData.reset();
2761 pageNb++;
2762
2763 if ( pageNb == pageIos.length )
2764 {
2765
2766 break;
2767 }
2768
2769 pageData = pageIos[pageNb].getData();
2770 pagePos = LINK_SIZE;
2771 nbStored -= remaining;
2772 remaining = pageData.capacity() - pagePos;
2773 }
2774 }
2775
2776
2777 position += bytes.length;
2778 }
2779 else
2780 {
2781
2782 position = store( position, 0, pageIos );
2783 }
2784
2785 return position;
2786 }
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798 private long store( long position, int value, PageIO... pageIos )
2799 {
2800
2801
2802 int pageNb = computePageNb( position );
2803
2804
2805 int pagePos = ( int ) ( position + ( pageNb + 1 ) * LONG_SIZE + INT_SIZE ) - pageNb * pageSize;
2806
2807
2808 ByteBuffer pageData = pageIos[pageNb].getData();
2809
2810
2811 int remaining = pageData.capacity() - pagePos;
2812
2813 if ( remaining < INT_SIZE )
2814 {
2815
2816
2817 switch ( remaining )
2818 {
2819 case 3:
2820 pageData.put( pagePos + 2, ( byte ) ( value >>> 8 ) );
2821
2822
2823 case 2:
2824 pageData.put( pagePos + 1, ( byte ) ( value >>> 16 ) );
2825
2826
2827 case 1:
2828 pageData.put( pagePos, ( byte ) ( value >>> 24 ) );
2829 break;
2830 }
2831
2832
2833 pageData = pageIos[pageNb + 1].getData();
2834 pagePos = LINK_SIZE;
2835
2836 switch ( remaining )
2837 {
2838 case 1:
2839 pageData.put( pagePos, ( byte ) ( value >>> 16 ) );
2840
2841
2842 case 2:
2843 pageData.put( pagePos + 2 - remaining, ( byte ) ( value >>> 8 ) );
2844
2845
2846 case 3:
2847 pageData.put( pagePos + 3 - remaining, ( byte ) ( value ) );
2848 break;
2849 }
2850 }
2851 else
2852 {
2853
2854 pageData.putInt( pagePos, value );
2855 }
2856
2857
2858 position += INT_SIZE;
2859
2860 return position;
2861 }
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873 private long store( long position, long value, PageIO... pageIos )
2874 {
2875
2876
2877 int pageNb = computePageNb( position );
2878
2879
2880 int pagePos = ( int ) ( position + ( pageNb + 1 ) * LONG_SIZE + INT_SIZE ) - pageNb * pageSize;
2881
2882
2883 ByteBuffer pageData = pageIos[pageNb].getData();
2884
2885
2886 int remaining = pageData.capacity() - pagePos;
2887
2888 if ( remaining < LONG_SIZE )
2889 {
2890
2891
2892 switch ( remaining )
2893 {
2894 case 7:
2895 pageData.put( pagePos + 6, ( byte ) ( value >>> 8 ) );
2896
2897
2898 case 6:
2899 pageData.put( pagePos + 5, ( byte ) ( value >>> 16 ) );
2900
2901
2902 case 5:
2903 pageData.put( pagePos + 4, ( byte ) ( value >>> 24 ) );
2904
2905
2906 case 4:
2907 pageData.put( pagePos + 3, ( byte ) ( value >>> 32 ) );
2908
2909
2910 case 3:
2911 pageData.put( pagePos + 2, ( byte ) ( value >>> 40 ) );
2912
2913
2914 case 2:
2915 pageData.put( pagePos + 1, ( byte ) ( value >>> 48 ) );
2916
2917
2918 case 1:
2919 pageData.put( pagePos, ( byte ) ( value >>> 56 ) );
2920 break;
2921 }
2922
2923
2924 pageData = pageIos[pageNb + 1].getData();
2925 pagePos = LINK_SIZE;
2926
2927 switch ( remaining )
2928 {
2929 case 1:
2930 pageData.put( pagePos, ( byte ) ( value >>> 48 ) );
2931
2932
2933 case 2:
2934 pageData.put( pagePos + 2 - remaining, ( byte ) ( value >>> 40 ) );
2935
2936
2937 case 3:
2938 pageData.put( pagePos + 3 - remaining, ( byte ) ( value >>> 32 ) );
2939
2940
2941 case 4:
2942 pageData.put( pagePos + 4 - remaining, ( byte ) ( value >>> 24 ) );
2943
2944
2945 case 5:
2946 pageData.put( pagePos + 5 - remaining, ( byte ) ( value >>> 16 ) );
2947
2948
2949 case 6:
2950 pageData.put( pagePos + 6 - remaining, ( byte ) ( value >>> 8 ) );
2951
2952
2953 case 7:
2954 pageData.put( pagePos + 7 - remaining, ( byte ) ( value ) );
2955 break;
2956 }
2957 }
2958 else
2959 {
2960
2961 pageData.putLong( pagePos, value );
2962 }
2963
2964
2965 position += LONG_SIZE;
2966
2967 return position;
2968 }
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980 <K, V> PageHolder<K, V> writePage( BTree<K, V> btree, Page<K, V> newPage,
2981 long newRevision ) throws IOException
2982 {
2983
2984 PageIO[] pageIos = serializePage( btree, newRevision, newPage );
2985
2986 if ( LOG_PAGES.isDebugEnabled() )
2987 {
2988 LOG_PAGES.debug( "Write data for '{}' btree", btree.getName() );
2989
2990 logPageIos( pageIos );
2991 }
2992
2993
2994 flushPages( pageIos );
2995
2996
2997 long offset = pageIos[0].getOffset();
2998 long lastOffset = pageIos[pageIos.length - 1].getOffset();
2999 PersistedPageHolder<K, V> pageHolder = new PersistedPageHolder<K, V>( btree, newPage, offset,
3000 lastOffset );
3001
3002 return pageHolder;
3003 }
3004
3005
3006 static void logPageIos( PageIO[] pageIos )
3007 {
3008 int pageNb = 0;
3009
3010 for ( PageIO pageIo : pageIos )
3011 {
3012 StringBuilder sb = new StringBuilder();
3013 sb.append( "PageIO[" ).append( pageNb ).append( "]:0x" );
3014 sb.append( Long.toHexString( pageIo.getOffset() ) ).append( "/" );
3015 sb.append( pageIo.getSize() );
3016 pageNb++;
3017
3018 ByteBuffer data = pageIo.getData();
3019
3020 int position = data.position();
3021 int dataLength = ( int ) pageIo.getSize() + 12;
3022
3023 if ( dataLength > data.limit() )
3024 {
3025 dataLength = data.limit();
3026 }
3027
3028 byte[] bytes = new byte[dataLength];
3029
3030 data.get( bytes );
3031 data.position( position );
3032 int pos = 0;
3033
3034 for ( byte b : bytes )
3035 {
3036 int mod = pos % 16;
3037
3038 switch ( mod )
3039 {
3040 case 0:
3041 sb.append( "\n " );
3042
3043 case 4:
3044 case 8:
3045 case 12:
3046 sb.append( " " );
3047 case 1:
3048 case 2:
3049 case 3:
3050 case 5:
3051 case 6:
3052 case 7:
3053 case 9:
3054 case 10:
3055 case 11:
3056 case 13:
3057 case 14:
3058 case 15:
3059 sb.append( Strings.dumpByte( b ) ).append( " " );
3060 }
3061 pos++;
3062 }
3063
3064 LOG_PAGES.debug( sb.toString() );
3065 }
3066 }
3067
3068
3069
3070
3071
3072
3073
3074
3075 private int computeNbPages( int dataSize )
3076 {
3077 if ( dataSize <= 0 )
3078 {
3079 return 0;
3080 }
3081
3082
3083
3084
3085
3086
3087
3088
3089 int availableSize = ( pageSize - LONG_SIZE );
3090 int nbNeededPages = 1;
3091
3092
3093 if ( dataSize > availableSize - INT_SIZE )
3094 {
3095 int remainingSize = dataSize - ( availableSize - INT_SIZE );
3096 nbNeededPages += remainingSize / availableSize;
3097 int remain = remainingSize % availableSize;
3098
3099 if ( remain > 0 )
3100 {
3101 nbNeededPages++;
3102 }
3103 }
3104
3105 return nbNeededPages;
3106 }
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116 private PageIO[] getFreePageIOs( int dataSize ) throws IOException
3117 {
3118 if ( dataSize == 0 )
3119 {
3120 return new PageIO[]
3121 {};
3122 }
3123
3124 int nbNeededPages = computeNbPages( dataSize );
3125
3126 PageIO[] pageIOs = new PageIO[nbNeededPages];
3127
3128
3129 pageIOs[0] = fetchNewPage();
3130 pageIOs[0].setSize( dataSize );
3131
3132 for ( int i = 1; i < nbNeededPages; i++ )
3133 {
3134 pageIOs[i] = fetchNewPage();
3135
3136
3137 pageIOs[i - 1].setNextPage( pageIOs[i].getOffset() );
3138 }
3139
3140 return pageIOs;
3141 }
3142
3143
3144
3145
3146
3147
3148
3149
3150 private PageIO fetchNewPage() throws IOException
3151 {
3152
3153 if ( firstFreePage == NO_PAGE )
3154 {
3155 nbCreatedPages.incrementAndGet();
3156
3157
3158
3159 PageIO newPage = new PageIO( endOfFileOffset );
3160
3161 endOfFileOffset += pageSize;
3162
3163 ByteBuffer data = ByteBuffer.allocateDirect( pageSize );
3164
3165 newPage.setData( data );
3166 newPage.setNextPage( NO_PAGE );
3167 newPage.setSize( 0 );
3168
3169 LOG.debug( "Requiring a new page at offset {}", newPage.getOffset() );
3170
3171 return newPage;
3172 }
3173 else
3174 {
3175 nbReusedPages.incrementAndGet();
3176
3177 freePageLock.lock();
3178
3179
3180 PageIO pageIo = fetchPage( firstFreePage );
3181
3182
3183 firstFreePage = pageIo.getNextPage();
3184
3185 freePageLock.unlock();
3186
3187
3188 ByteBuffer data = ByteBuffer.allocateDirect( pageSize );
3189 pageIo.setData( data );
3190
3191 pageIo.setNextPage( NO_PAGE );
3192 pageIo.setSize( 0 );
3193
3194 LOG.debug( "Reused page at offset {}", pageIo.getOffset() );
3195
3196 return pageIo;
3197 }
3198 }
3199
3200
3201
3202
3203
3204
3205
3206
3207 PageIO fetchPage( long offset ) throws IOException, EndOfFileExceededException
3208 {
3209 checkOffset( offset );
3210
3211 if ( fileChannel.size() < offset + pageSize )
3212 {
3213
3214 throw new EndOfFileExceededException( "We are fetching a page on " + offset +
3215 " when the file's size is " + fileChannel.size() );
3216 }
3217 else
3218 {
3219
3220 fileChannel.position( offset );
3221
3222 ByteBuffer data = ByteBuffer.allocate( pageSize );
3223 fileChannel.read( data );
3224 data.rewind();
3225
3226 PageIO readPage = new PageIO( offset );
3227 readPage.setData( data );
3228
3229 return readPage;
3230 }
3231 }
3232
3233
3234
3235
3236
3237 public int getPageSize()
3238 {
3239 return pageSize;
3240 }
3241
3242
3243
3244
3245
3246
3247
3248 void setPageSize( int pageSize )
3249 {
3250 if ( this.pageSize >= 13 )
3251 {
3252 this.pageSize = pageSize;
3253 }
3254 else
3255 {
3256 this.pageSize = DEFAULT_PAGE_SIZE;
3257 }
3258 }
3259
3260
3261
3262
3263
3264 public void close() throws IOException
3265 {
3266 beginTransaction();
3267
3268
3269 for ( BTree<Object, Object> tree : managedBtrees.values() )
3270 {
3271 tree.close();
3272 }
3273
3274
3275 copiedPageBtree.close();
3276 btreeOfBtrees.close();
3277
3278 managedBtrees.clear();
3279
3280
3281 fileChannel.force( true );
3282
3283
3284 fileChannel.close();
3285
3286 commit();
3287 }
3288
3289
3290 private static final byte[] HEX_CHAR = new byte[]
3291 { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
3292
3293
3294 public static String dump( byte octet )
3295 {
3296 return new String( new byte[]
3297 { HEX_CHAR[( octet & 0x00F0 ) >> 4], HEX_CHAR[octet & 0x000F] } );
3298 }
3299
3300
3301
3302
3303
3304 private void dump( PageIO pageIo )
3305 {
3306 ByteBuffer buffer = pageIo.getData();
3307 buffer.mark();
3308 byte[] longBuffer = new byte[LONG_SIZE];
3309 byte[] intBuffer = new byte[INT_SIZE];
3310
3311
3312 buffer.get( longBuffer );
3313 long nextOffset = LongSerializer.deserialize( longBuffer );
3314
3315
3316 buffer.get( intBuffer );
3317 int size = IntSerializer.deserialize( intBuffer );
3318
3319 buffer.reset();
3320
3321 System.out.println( "PageIO[" + Long.toHexString( pageIo.getOffset() ) + "], size = " + size + ", NEXT PageIO:"
3322 + Long.toHexString( nextOffset ) );
3323 System.out.println( " 0 1 2 3 4 5 6 7 8 9 A B C D E F " );
3324 System.out.println( "+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+" );
3325
3326 for ( int i = 0; i < buffer.limit(); i += 16 )
3327 {
3328 System.out.print( "|" );
3329
3330 for ( int j = 0; j < 16; j++ )
3331 {
3332 System.out.print( dump( buffer.get() ) );
3333
3334 if ( j == 15 )
3335 {
3336 System.out.println( "|" );
3337 }
3338 else
3339 {
3340 System.out.print( " " );
3341 }
3342 }
3343 }
3344
3345 System.out.println( "+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+" );
3346
3347 buffer.reset();
3348 }
3349
3350
3351
3352
3353
3354
3355 public void dump()
3356 {
3357 System.out.println( "/---------------------------- Dump ----------------------------\\" );
3358
3359 try
3360 {
3361 RandomAccessFile randomFile = new RandomAccessFile( file, "r" );
3362 FileChannel fileChannel = randomFile.getChannel();
3363
3364 ByteBuffer recordManagerHeader = ByteBuffer.allocate( RECORD_MANAGER_HEADER_SIZE );
3365
3366
3367 fileChannel.read( recordManagerHeader );
3368
3369 recordManagerHeader.rewind();
3370
3371
3372 long fileSize = fileChannel.size();
3373 int pageSize = recordManagerHeader.getInt();
3374 long nbPages = fileSize / pageSize;
3375
3376
3377 int nbBtree = recordManagerHeader.getInt();
3378
3379
3380 long firstFreePage = recordManagerHeader.getLong();
3381
3382
3383 long currentBtreeOfBtreesPage = recordManagerHeader.getLong();
3384
3385
3386 long previousBtreeOfBtreesPage = recordManagerHeader.getLong();
3387
3388
3389 long currentCopiedPagesBtreePage = recordManagerHeader.getLong();
3390
3391
3392 long previousCopiedPagesBtreePage = recordManagerHeader.getLong();
3393
3394 System.out.println( " RecordManager" );
3395 System.out.println( " -------------" );
3396 System.out.println( " Size = 0x" + Long.toHexString( fileSize ) );
3397 System.out.println( " NbPages = " + nbPages );
3398 System.out.println( " Header " );
3399 System.out.println( " page size : " + pageSize );
3400 System.out.println( " nbTree : " + nbBtree );
3401 System.out.println( " firstFreePage : 0x" + Long.toHexString( firstFreePage ) );
3402 System.out.println( " current BOB : 0x" + Long.toHexString( currentBtreeOfBtreesPage ) );
3403 System.out.println( " previous BOB : 0x" + Long.toHexString( previousBtreeOfBtreesPage ) );
3404 System.out.println( " current CopiedPages : 0x" + Long.toHexString( currentCopiedPagesBtreePage ) );
3405 System.out.println( " previous CopiedPages : 0x" + Long.toHexString( previousCopiedPagesBtreePage ) );
3406
3407
3408 dumpFreePages( firstFreePage );
3409
3410
3411 dumpBtreeHeader( currentBtreeOfBtreesPage );
3412
3413
3414 if ( previousBtreeOfBtreesPage != NO_PAGE )
3415 {
3416 dumpBtreeHeader( previousBtreeOfBtreesPage );
3417 }
3418
3419
3420 dumpBtreeHeader( currentCopiedPagesBtreePage );
3421
3422
3423 if ( previousCopiedPagesBtreePage != NO_PAGE )
3424 {
3425 dumpBtreeHeader( previousCopiedPagesBtreePage );
3426 }
3427
3428
3429 randomFile.close();
3430 System.out.println( "\\---------------------------- Dump ----------------------------/" );
3431 }
3432 catch ( IOException ioe )
3433 {
3434 System.out.println( "Exception while dumping the file : " + ioe.getMessage() );
3435 }
3436 }
3437
3438
3439
3440
3441
3442 private void dumpFreePages( long freePageOffset ) throws EndOfFileExceededException, IOException
3443 {
3444 System.out.println( "\n FreePages : " );
3445 int pageNb = 1;
3446
3447 while ( freePageOffset != NO_PAGE )
3448 {
3449 PageIO pageIo = fetchPage( freePageOffset );
3450
3451 System.out.println( " freePage[" + pageNb + "] : 0x" + Long.toHexString( pageIo.getOffset() ) );
3452
3453 freePageOffset = pageIo.getNextPage();
3454 pageNb++;
3455 }
3456 }
3457
3458
3459
3460
3461
3462 private long dumpBtreeHeader( long btreeOffset ) throws EndOfFileExceededException, IOException
3463 {
3464
3465 PageIO[] pageIos = readPageIOs( btreeOffset, Long.MAX_VALUE );
3466
3467 long dataPos = 0L;
3468
3469
3470 long revision = readLong( pageIos, dataPos );
3471 dataPos += LONG_SIZE;
3472
3473
3474 long nbElems = readLong( pageIos, dataPos );
3475 dataPos += LONG_SIZE;
3476
3477
3478 long rootPageOffset = readLong( pageIos, dataPos );
3479 dataPos += LONG_SIZE;
3480
3481
3482 int btreePageSize = readInt( pageIos, dataPos );
3483 dataPos += INT_SIZE;
3484
3485
3486 ByteBuffer btreeNameBytes = readBytes( pageIos, dataPos );
3487 dataPos += INT_SIZE + btreeNameBytes.limit();
3488 String btreeName = Strings.utf8ToString( btreeNameBytes );
3489
3490
3491 ByteBuffer keySerializerBytes = readBytes( pageIos, dataPos );
3492 dataPos += INT_SIZE + keySerializerBytes.limit();
3493
3494 String keySerializerFqcn = "";
3495
3496 if ( keySerializerBytes != null )
3497 {
3498 keySerializerFqcn = Strings.utf8ToString( keySerializerBytes );
3499 }
3500
3501
3502 ByteBuffer valueSerializerBytes = readBytes( pageIos, dataPos );
3503
3504 String valueSerializerFqcn = "";
3505 dataPos += INT_SIZE + valueSerializerBytes.limit();
3506
3507 if ( valueSerializerBytes != null )
3508 {
3509 valueSerializerFqcn = Strings.utf8ToString( valueSerializerBytes );
3510 }
3511
3512
3513 int allowDuplicates = readInt( pageIos, dataPos );
3514 boolean dupsAllowed = allowDuplicates != 0;
3515
3516 dataPos += INT_SIZE;
3517
3518
3519
3520
3521
3522 if ( LOG.isDebugEnabled() )
3523 {
3524 StringBuilder sb = new StringBuilder();
3525 boolean isFirst = true;
3526
3527 for ( PageIO pageIo : pageIos )
3528 {
3529 if ( isFirst )
3530 {
3531 isFirst = false;
3532 }
3533 else
3534 {
3535 sb.append( ", " );
3536 }
3537
3538 sb.append( "0x" ).append( Long.toHexString( pageIo.getOffset() ) );
3539 }
3540
3541 String pageIoList = sb.toString();
3542
3543 LOG.debug( " PageIOs[{}] = {}", pageIos.length, pageIoList );
3544
3545
3546 LOG.debug( " dataSize = {}", pageIos[0].getSize() );
3547
3548 LOG.debug( " B-tree '{}'", btreeName );
3549 LOG.debug( " revision : {}", revision );
3550 LOG.debug( " nbElems : {}", nbElems );
3551 LOG.debug( " rootPageOffset : 0x{}", Long.toHexString( rootPageOffset ) );
3552 LOG.debug( " B-tree page size : {}", btreePageSize );
3553 LOG.debug( " keySerializer : '{}'", keySerializerFqcn );
3554 LOG.debug( " valueSerializer : '{}'", valueSerializerFqcn );
3555 LOG.debug( " dups allowed : {}", dupsAllowed );
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565 }
3566
3567 return rootPageOffset;
3568 }
3569
3570
3571
3572
3573
3574
3575
3576 public int getNbManagedTrees()
3577 {
3578 return nbBtree;
3579 }
3580
3581
3582
3583
3584
3585
3586
3587 public Set<String> getManagedTrees()
3588 {
3589 Set<String> btrees = new HashSet<String>( managedBtrees.keySet() );
3590
3591 return btrees;
3592 }
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603 void storeCopiedPages( String name, long revision, long[] copiedPages ) throws IOException
3604 {
3605 RevisionName revisionName = new RevisionName( revision, name );
3606
3607 copiedPageBtree.insert( revisionName, copiedPages );
3608 }
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618 <K, V> void storeRootPage( BTree<K, V> btree, Page<K, V> rootPage ) throws IOException
3619 {
3620 if ( !isKeepRevisions() )
3621 {
3622 return;
3623 }
3624
3625 if ( btree == copiedPageBtree )
3626 {
3627 return;
3628 }
3629
3630 NameRevision nameRevision = new NameRevision( btree.getName(), rootPage.getRevision() );
3631
3632 ( ( AbstractBTree<NameRevision, Long> ) btreeOfBtrees ).insert( nameRevision,
3633 ( ( AbstractPage<K, V> ) rootPage ).getOffset(), 0 );
3634
3635 if ( LOG_CHECK.isDebugEnabled() )
3636 {
3637 MavibotInspector.check( this );
3638 }
3639 }
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651 <K, V> Page<K, V> getRootPage( BTree<K, V> btree, long revision ) throws KeyNotFoundException,
3652 IOException
3653 {
3654 if ( btree.getRevision() == revision )
3655 {
3656
3657 return btree.getRootPage();
3658 }
3659
3660
3661 NameRevision nameRevision = new NameRevision( btree.getName(), revision );
3662 long btreeHeaderOffset = btreeOfBtrees.get( nameRevision );
3663
3664
3665 Page<K, V> btreeRoot = readRootPage( btree, btreeHeaderOffset );
3666
3667 return btreeRoot;
3668 }
3669
3670
3671
3672
3673
3674 private <K, V> Page<K, V> readRootPage( BTree<K, V> btree, long btreeHeaderOffset )
3675 throws EndOfFileExceededException, IOException
3676 {
3677
3678 PageIO[] btreeHeaderPageIos = readPageIOs( btreeHeaderOffset, Long.MAX_VALUE );
3679 long dataPos = LONG_SIZE + LONG_SIZE;
3680
3681
3682 long rootPageOffset = readLong( btreeHeaderPageIos, dataPos );
3683
3684
3685 PageIO[] rootPageIos = readPageIOs( rootPageOffset, Long.MAX_VALUE );
3686
3687
3688 Page<K, V> btreeRoot = readPage( btree, rootPageIos );
3689
3690 return btreeRoot;
3691 }
3692
3693
3694
3695
3696
3697
3698
3699
3700 public <K, V> BTree<K, V> getManagedTree( String name )
3701 {
3702 return ( BTree<K, V> ) managedBtrees.get( name );
3703 }
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718 <K, V> void freePages( BTree<K, V> btree, long revision, List<Page<K, V>> pages )
3719 throws EndOfFileExceededException, IOException
3720 {
3721 if ( ( pages == null ) || pages.isEmpty() )
3722 {
3723 return;
3724 }
3725
3726 if ( !keepRevisions )
3727 {
3728
3729
3730 if ( LOG.isDebugEnabled() )
3731 {
3732 LOG.debug( "Freeing the following pages :" );
3733
3734 for ( Page<K, V> page : pages )
3735 {
3736 LOG.debug( " {}", page );
3737 }
3738 }
3739
3740 for ( Page<K, V> page : pages )
3741 {
3742 long pageOffset = ( ( AbstractPage<K, V> ) page ).getOffset();
3743
3744 PageIO[] pageIos = readPageIOs( pageOffset, Long.MAX_VALUE );
3745
3746 for ( PageIO pageIo : pageIos )
3747 {
3748 freedPages.add( pageIo );
3749 }
3750 }
3751 }
3752 else
3753 {
3754
3755
3756 if ( LOG.isDebugEnabled() )
3757 {
3758 LOG.debug( "Moving the following pages to the CopiedBtree :" );
3759
3760 for ( Page<K, V> page : pages )
3761 {
3762 LOG.debug( " {}", page );
3763 }
3764 }
3765
3766 long[] pageOffsets = new long[pages.size()];
3767 int pos = 0;
3768
3769 for ( Page<K, V> page : pages )
3770 {
3771 pageOffsets[pos++] = ( ( AbstractPage<K, V> ) page ).offset;
3772 }
3773
3774 if ( ( btree.getType() != BTreeTypeEnum.BTREE_OF_BTREES )
3775 && ( btree.getType() != BTreeTypeEnum.COPIED_PAGES_BTREE ) )
3776 {
3777
3778 RevisionName revisionName = new RevisionName( revision, btree.getName() );
3779
3780 copiedPageBtree.insert( revisionName, pageOffsets );
3781
3782
3783 currentCopiedPagesBtreeOffset = ( ( PersistedBTree<RevisionName, long[]> ) copiedPageBtree ).getBtreeOffset();
3784 }
3785 else
3786 {
3787
3788 for ( long pageOffset : pageOffsets )
3789 {
3790 PageIO[] pageIos = readPageIOs( pageOffset, Long.MAX_VALUE );
3791
3792 for ( PageIO pageIo : pageIos )
3793 {
3794 freedPages.add( pageIo );
3795 }
3796 }
3797 }
3798 }
3799 }
3800
3801
3802
3803
3804
3805
3806
3807
3808 void free( PageIO pageIo ) throws IOException
3809 {
3810 freePageLock.lock();
3811
3812
3813
3814
3815 pageIo.setNextPage( firstFreePage );
3816
3817 LOG.debug( "Flushing the first free page" );
3818
3819
3820
3821 flushPages( pageIo );
3822
3823
3824 firstFreePage = pageIo.getOffset();
3825
3826 freePageLock.unlock();
3827 }
3828
3829
3830
3831
3832
3833
3834
3835
3836 void free( long... offsets ) throws IOException
3837 {
3838 freePageLock.lock();
3839
3840 List<PageIO> pageIos = new ArrayList<PageIO>();
3841 int pageIndex = 0;
3842 for ( int i = 0; i < offsets.length; i++ )
3843 {
3844 PageIO[] ios = readPageIOs( offsets[i], Long.MAX_VALUE );
3845 for ( PageIO io : ios )
3846 {
3847 pageIos.add( io );
3848
3849 if ( pageIndex > 0 )
3850 {
3851 pageIos.get( pageIndex - 1 ).setNextPage( io.getOffset() );
3852 }
3853
3854 pageIndex++;
3855 }
3856 }
3857
3858
3859
3860
3861
3862 pageIos.get( pageIndex - 1 ).setNextPage( firstFreePage );
3863
3864 LOG.debug( "Flushing the first free page" );
3865
3866
3867
3868 flushPages( pageIos.toArray( new PageIO[0] ) );
3869
3870
3871 firstFreePage = pageIos.get( 0 ).getOffset();
3872
3873 freePageLock.unlock();
3874 }
3875
3876
3877
3878
3879
3880 public boolean isKeepRevisions()
3881 {
3882 return keepRevisions;
3883 }
3884
3885
3886
3887
3888
3889 public void setKeepRevisions( boolean keepRevisions )
3890 {
3891 this.keepRevisions = keepRevisions;
3892 }
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906 @SuppressWarnings("all")
3907 public <K, V> BTree<K, V> addBTree( String name, ElementSerializer<K> keySerializer,
3908 ElementSerializer<V> valueSerializer, boolean allowDuplicates )
3909 throws IOException, BTreeAlreadyManagedException
3910 {
3911 PersistedBTreeConfiguration config = new PersistedBTreeConfiguration();
3912
3913 config.setName( name );
3914 config.setKeySerializer( keySerializer );
3915 config.setValueSerializer( valueSerializer );
3916 config.setAllowDuplicates( allowDuplicates );
3917
3918 BTree btree = new PersistedBTree( config );
3919 manage( btree );
3920
3921 if ( LOG_CHECK.isDebugEnabled() )
3922 {
3923 MavibotInspector.check( this );
3924 }
3925
3926 return btree;
3927 }
3928
3929
3930
3931
3932
3933 <K, V> void releaseTransaction( ReadTransaction<K, V> readTransaction )
3934 {
3935 RevisionName revisionName = new RevisionName(
3936 readTransaction.getRevision(),
3937 readTransaction.getBtreeHeader().getBtree().getName() );
3938
3939 }
3940
3941
3942
3943
3944
3945 public BTreeHeader getBTreeHeader( String name )
3946 {
3947
3948 btreeHeadersLock.readLock().lock();
3949
3950
3951 BTreeHeader<?, ?> btreeHeader = currentBTreeHeaders.get( name );
3952
3953
3954 btreeHeadersLock.readLock().unlock();
3955
3956 return btreeHeader;
3957 }
3958
3959
3960
3961
3962
3963 public BTreeHeader getNewBTreeHeader( String name )
3964 {
3965
3966 BTreeHeader<?, ?> btreeHeader = newBTreeHeaders.get( name );
3967
3968 return btreeHeader;
3969 }
3970
3971
3972
3973
3974
3975 public void updateNewBTreeHeaders( BTreeHeader btreeHeader )
3976 {
3977 newBTreeHeaders.put( btreeHeader.getBtree().getName(), btreeHeader );
3978 }
3979
3980
3981
3982
3983
3984
3985 private void swapCurrentBtreeHeaders()
3986 {
3987
3988 Map<String, BTreeHeader<?, ?>> tmp = currentBTreeHeaders;
3989
3990
3991 btreeHeadersLock.writeLock().lock();
3992
3993
3994 currentBTreeHeaders = newBTreeHeaders;
3995
3996
3997 btreeHeadersLock.writeLock().unlock();
3998
3999
4000 tmp.clear();
4001 tmp.putAll( currentBTreeHeaders );
4002
4003
4004 newBTreeHeaders = tmp;
4005 }
4006
4007
4008
4009
4010
4011
4012 private void revertBtreeHeaders()
4013 {
4014
4015 newBTreeHeaders.clear();
4016
4017
4018 newBTreeHeaders.putAll( currentBTreeHeaders );
4019 }
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029 <K, V> BTree<V, V> loadDupsBtree( long btreeHeaderOffset, BTree<K, V> parentBtree )
4030 {
4031 PageIO[] pageIos = null;
4032 try
4033 {
4034 pageIos = readPageIOs( btreeHeaderOffset, Long.MAX_VALUE );
4035
4036 BTree<V, V> subBtree = BTreeFactory.<V, V> createPersistedBTree( BTreeTypeEnum.PERSISTED_SUB );
4037 loadBtree( pageIos, subBtree, parentBtree );
4038
4039 return subBtree;
4040 }
4041 catch ( Exception e )
4042 {
4043
4044 throw new BTreeCreationException( e );
4045 }
4046 }
4047
4048
4049 private void checkFreePages() throws EndOfFileExceededException, IOException
4050 {
4051
4052
4053
4054 Set<Long> freePageOffsets = new HashSet<Long>();
4055
4056 long currentFreePageOffset = firstFreePage;
4057
4058 while ( currentFreePageOffset != NO_PAGE )
4059 {
4060
4061
4062 if ( ( currentFreePageOffset % pageSize ) != 0 )
4063 {
4064 throw new InvalidOffsetException( "Wrong offset : " + Long.toHexString( currentFreePageOffset ) );
4065 }
4066
4067 if ( freePageOffsets.contains( currentFreePageOffset ) )
4068 {
4069 throw new InvalidOffsetException( "Offset : " + Long.toHexString( currentFreePageOffset )
4070 + " already read, there is a cycle" );
4071 }
4072
4073 freePageOffsets.add( currentFreePageOffset );
4074 PageIO pageIO = fetchPage( currentFreePageOffset );
4075
4076 currentFreePageOffset = pageIO.getNextPage();
4077 }
4078
4079 return;
4080 }
4081
4082
4083
4084
4085
4086
4087
4088
4089 void setPageReclaimerThreshold( int pageReclaimerThreshold )
4090 {
4091 this.pageReclaimerThreshold = pageReclaimerThreshold;
4092 }
4093
4094
4095 void _disableReclaimer( boolean toggle )
4096 {
4097 this.disableReclaimer = toggle;
4098 }
4099
4100
4101
4102
4103
4104 public String toString()
4105 {
4106 StringBuilder sb = new StringBuilder();
4107
4108 sb.append( "RM free pages : [" );
4109
4110 if ( firstFreePage != NO_PAGE )
4111 {
4112 long current = firstFreePage;
4113 boolean isFirst = true;
4114
4115 while ( current != NO_PAGE )
4116 {
4117 if ( isFirst )
4118 {
4119 isFirst = false;
4120 }
4121 else
4122 {
4123 sb.append( ", " );
4124 }
4125
4126 PageIO pageIo;
4127
4128 try
4129 {
4130 pageIo = fetchPage( current );
4131 sb.append( pageIo.getOffset() );
4132 current = pageIo.getNextPage();
4133 }
4134 catch ( EndOfFileExceededException e )
4135 {
4136 e.printStackTrace();
4137 }
4138 catch ( IOException e )
4139 {
4140 e.printStackTrace();
4141 }
4142
4143 }
4144 }
4145
4146 sb.append( "]" );
4147
4148 return sb.toString();
4149 }
4150 }