1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.mavibot.btree;
21
22
23 import java.io.Closeable;
24 import java.io.IOException;
25 import java.nio.ByteBuffer;
26 import java.nio.channels.FileChannel;
27 import java.util.Map;
28 import java.util.concurrent.ConcurrentLinkedQueue;
29 import java.util.concurrent.locks.ReentrantLock;
30
31 import net.sf.ehcache.Cache;
32 import net.sf.ehcache.Status;
33 import net.sf.ehcache.config.CacheConfiguration;
34
35 import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39
40
41
42
43
44
45
46
47
48 public class PersistedBTree<K, V> extends AbstractBTree<K, V> implements Closeable
49 {
50
51 protected static final Logger LOG = LoggerFactory.getLogger( PersistedBTree.class );
52
53
54 private RecordManager recordManager;
55
56
57 protected Cache cache;
58
59
60 static final int DEFAULT_CACHE_SIZE = 1000;
61
62
63 protected int cacheSize = DEFAULT_CACHE_SIZE;
64
65
66 private boolean isSubBtree = false;
67
68
69 private static final int DEFAULT_VALUE_THRESHOLD_UP = 8;
70
71
72 private static final int DEFAULT_VALUE_THRESHOLD_LOW = 1;
73
74
75
76
77
78
79 protected ReentrantLock createTransaction = new ReentrantLock();
80
81
82
83
84
85
86 {
87 btreeHeader = new BTreeHeader();
88 setType( BTreeTypeEnum.PERSISTED );
89 }
90
91
92
93
94
95
96
97
98
99 {
100 super();
101 String name = configuration.getName();
102
103 if ( name == null )
104 {
105 throw new IllegalArgumentException( "BTree name cannot be null" );
106 }
107
108 btreeHeader = new BTreeHeader();
109 btreeHeader.setName( name );
110 btreeHeader.setPageSize( configuration.getPageSize() );
111 isSubBtree = configuration.isSubBtree();
112
113 keySerializer = configuration.getKeySerializer();
114 btreeHeader.setKeySerializerFQCN( keySerializer.getClass().getName() );
115
116 valueSerializer = configuration.getValueSerializer();
117 btreeHeader.setValueSerializerFQCN( valueSerializer.getClass().getName() );
118
119 readTimeOut = configuration.getReadTimeOut();
120 writeBufferSize = configuration.getWriteBufferSize();
121 btreeHeader.setAllowDuplicates( configuration.isAllowDuplicates() );
122 cacheSize = configuration.getCacheSize();
123 setType( BTreeTypeEnum.PERSISTED );
124
125 if ( keySerializer.getComparator() == null )
126 {
127 throw new IllegalArgumentException( "Comparator should not be null" );
128 }
129
130
131
132 rootPage = new PersistedLeaf<K, V>( this );
133
134 if ( isSubBtree )
135 {
136
137 this.cache = ( ( PersistedBTree<K, V> ) configuration.getParentBTree() ).getCache();
138 this.writeLock = ( ( PersistedBTree<K, V> ) configuration.getParentBTree() ).getWriteLock();
139 readTransactions = new ConcurrentLinkedQueue<ReadTransaction<K, V>>();
140 }
141
142
143 init();
144 }
145
146
147
148
149
150
151
152 public void init()
153 {
154 if ( !isSubBtree )
155 {
156
157
158
159 readTransactions = new ConcurrentLinkedQueue<ReadTransaction<K, V>>();
160
161 writeLock = new ReentrantLock();
162
163
164 CacheConfiguration cacheConfiguration = new CacheConfiguration();
165 cacheConfiguration.setName( "pages" );
166 cacheConfiguration.setEternal( true );
167 cacheConfiguration.setOverflowToDisk( false );
168 cacheConfiguration.setCacheLoaderTimeoutMillis( 0 );
169 cacheConfiguration.setMaxElementsInMemory( cacheSize );
170 cacheConfiguration.setMemoryStoreEvictionPolicy( "LRU" );
171
172 cache = new Cache( cacheConfiguration );
173 cache.initialise();
174 }
175
176
177
178
179 }
180
181
182
183
184
185
186 {
187 return cache;
188 }
189
190
191
192
193
194
195 {
196 return writeLock;
197 }
198
199
200
201
202
203
204 {
205 return readTransactions;
206 }
207
208
209
210
211
212 public void close() throws IOException
213 {
214
215
216
217
218
219 if ( cache.getStatus() == Status.STATUS_ALIVE )
220 {
221 cache.removeAll();
222 }
223
224 cache.dispose();
225
226 rootPage = null;
227 }
228
229
230
231
232
233
234 {
235 return btreeHeader.getBTreeOffset();
236 }
237
238
239
240
241
242
243 {
244 btreeHeader.setBTreeOffset( btreeOffset );
245 }
246
247
248
249
250
251
252 {
253 return btreeHeader.getRootPageOffset();
254 }
255
256
257
258
259
260
261 {
262 btreeHeader.setRootPageOffset( rootPageOffset );
263 }
264
265
266
267
268
269
270 {
271 return btreeHeader.getNextBTreeOffset();
272 }
273
274
275
276
277
278
279 {
280 btreeHeader.setNextBTreeOffset( nextBTreeOffset );
281 }
282
283
284
285
286
287
288
289
290 {
291 return recordManager;
292 }
293
294
295
296
297
298
299
300
301 {
302 this.recordManager = recordManager;
303 }
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318 protected Tuple<K, V> delete( K key, V value, long revision ) throws IOException
319 {
320 writeLock.lock();
321
322 try
323 {
324
325
326 Tuple<K, V> tuple = null;
327
328
329
330 DeleteResult<K, V> result = rootPage.delete( revision, key, value, null, -1 );
331
332 if ( result instanceof NotPresentResult )
333 {
334
335 return null;
336 }
337
338
339 Page<K, V> oldRootPage = rootPage;
340
341 if ( result instanceof RemoveResult )
342 {
343
344 RemoveResult<K, V> removeResult = ( RemoveResult<K, V> ) result;
345
346 Page<K, V> modifiedPage = removeResult.getModifiedPage();
347
348
349
350
351 PageHolder<K, V> holder = writePage( modifiedPage, revision );
352
353
354 ( ( AbstractPage<K, V> ) modifiedPage ).setOffset( ( ( PersistedPageHolder<K, V> ) holder )
355 .getOffset() );
356
357
358 ( ( AbstractPage<K, V> ) modifiedPage )
359 .setLastOffset( ( ( PersistedPageHolder<K, V> ) holder )
360 .getLastOffset() );
361
362
363 rootPage = modifiedPage;
364 tuple = removeResult.getRemovedElement();
365 }
366
367
368 if ( tuple != null )
369 {
370 btreeHeader.decrementNbElems();
371
372
373
374 recordManager.updateBtreeHeader( this, ( ( AbstractPage<K, V> ) rootPage ).getOffset() );
375 }
376
377 recordManager.addFreePages( this, result.getCopiedPages() );
378
379
380 recordManager.updateRecordManagerHeader();
381
382
383 recordManager.storeRootPage( this, rootPage );
384
385
386 return tuple;
387 }
388 finally
389 {
390
391 writeLock.unlock();
392 }
393 }
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410 {
411 if ( key == null )
412 {
413 throw new IllegalArgumentException( "Key must not be null" );
414 }
415
416
417
418 V modifiedValue = null;
419
420
421
422
423 InsertResult<K, V> result = rootPage.insert( revision, key, value );
424
425 if ( result instanceof ModifyResult )
426 {
427 ModifyResult<K, V> modifyResult = ( ( ModifyResult<K, V> ) result );
428
429 Page<K, V> modifiedPage = modifyResult.getModifiedPage();
430
431
432
433
434 writePage( modifiedPage, revision );
435
436
437
438 rootPage = modifiedPage;
439
440 modifiedValue = modifyResult.getModifiedValue();
441 }
442 else
443 {
444
445
446 SplitResult<K, V> splitResult = ( ( SplitResult<K, V> ) result );
447
448 K pivot = splitResult.getPivot();
449 Page<K, V> leftPage = splitResult.getLeftPage();
450 Page<K, V> rightPage = splitResult.getRightPage();
451 Page<K, V> newRootPage = null;
452
453
454
455 PageHolder<K, V> holderLeft = writePage( leftPage, revision );
456
457 PageHolder<K, V> holderRight = writePage( rightPage, revision );
458
459
460 newRootPage = new PersistedNode<K, V>( this, revision, pivot, holderLeft, holderRight );
461
462
463
464 PageHolder<K, V> holder = writePage( newRootPage, revision );
465
466 rootPage = newRootPage;
467 }
468
469
470
471 if ( modifiedValue == null )
472 {
473 btreeHeader.incrementNbElems();
474 }
475
476
477
478 if ( ( writeTransaction == null ) || !writeTransaction.isStarted() )
479 {
480 recordManager.updateRecordManagerHeader();
481
482
483 recordManager.updateBtreeHeader( this, ( ( AbstractPage<K, V> ) rootPage ).getOffset() );
484
485
486 recordManager.addFreePages( this, result.getCopiedPages() );
487
488
489 recordManager.storeRootPage( this, rootPage );
490 }
491
492
493 return result;
494 }
495
496
497
498
499
500
501
502
503
504
505 private void writeBuffer( FileChannel channel, ByteBuffer bb, byte[] buffer ) throws IOException
506 {
507 int size = buffer.length;
508 int pos = 0;
509
510
511 do
512 {
513 if ( bb.remaining() >= size )
514 {
515
516 bb.put( buffer, pos, size );
517 size = 0;
518 }
519 else
520 {
521
522 int len = bb.remaining();
523 size -= len;
524 bb.put( buffer, pos, len );
525 pos += len;
526
527 bb.flip();
528
529 channel.write( bb );
530
531 bb.clear();
532 }
533 }
534 while ( size > 0 );
535 }
536
537
538
539
540
541
542 private PageHolder<K, V> writePage( Page<K, V> modifiedPage, long revision ) throws IOException
543 {
544 if ( ( writeTransaction != null ) && writeTransaction.isStarted() )
545 {
546 Map<Page<?, ?>, BTree<?, ?>> pendingPages = recordManager.getPendingPages();
547 pendingPages.put( modifiedPage, this );
548
549 PageHolder<K, V> pageHolder = new PageHolder<K, V>( this, modifiedPage );
550
551 return pageHolder;
552 }
553 else
554 {
555 PageHolder<K, V> pageHolder = recordManager.writePage( this, modifiedPage, revision );
556
557 return pageHolder;
558 }
559 }
560
561
562
563
564
565
566
567
568
569 public Page<K, V> getRootPage( long revision ) throws IOException, KeyNotFoundException
570 {
571 return recordManager.getRootPage( this, revision );
572 }
573
574
575
576
577
578 public void beginTransaction()
579 {
580 createTransaction.lock();
581
582 if ( writeTransaction == null )
583 {
584 writeTransaction = new WriteTransaction( recordManager );
585 }
586
587 createTransaction.unlock();
588
589 writeTransaction.start();
590 }
591
592
593
594
595
596 public void commit()
597 {
598 createTransaction.lock();
599
600 if ( writeTransaction == null )
601 {
602 writeTransaction = new WriteTransaction( recordManager );
603 }
604
605 createTransaction.unlock();
606
607 writeTransaction.commit();
608 }
609
610
611
612
613
614 public void rollback()
615 {
616 createTransaction.lock();
617
618 if ( writeTransaction == null )
619 {
620 writeTransaction = new WriteTransaction( recordManager );
621 }
622
623 createTransaction.unlock();
624
625 writeTransaction.rollback();
626 }
627
628
629
630
631
632
633
634 public String toString()
635 {
636 StringBuilder sb = new StringBuilder();
637
638 sb.append( "Managed BTree" );
639 sb.append( "[" ).append( btreeHeader.getName() ).append( "]" );
640 sb.append( "( pageSize:" ).append( btreeHeader.getPageSize() );
641
642 if ( rootPage != null )
643 {
644 sb.append( ", nbEntries:" ).append( btreeHeader.getNbElems() );
645 }
646 else
647 {
648 sb.append( ", nbEntries:" ).append( 0 );
649 }
650
651 sb.append( ", comparator:" );
652
653 if ( keySerializer.getComparator() == null )
654 {
655 sb.append( "null" );
656 }
657 else
658 {
659 sb.append( keySerializer.getComparator().getClass().getSimpleName() );
660 }
661
662 sb.append( ", DuplicatesAllowed: " ).append( btreeHeader.isAllowDuplicates() );
663
664 sb.append( ") : \n" );
665 sb.append( rootPage.dumpPage( "" ) );
666
667 return sb.toString();
668 }
669 }