2929import java .io .ObjectOutputStream ;
3030import java .io .Serializable ;
3131import java .nio .ByteBuffer ;
32+ import java .security .NoSuchAlgorithmException ;
3233import java .util .ArrayList ;
3334import java .util .Comparator ;
3435import java .util .HashSet ;
6970import org .apache .hadoop .hbase .io .hfile .CacheableDeserializerIdManager ;
7071import org .apache .hadoop .hbase .io .hfile .CachedBlock ;
7172import org .apache .hadoop .hbase .io .hfile .HFileBlock ;
73+ import org .apache .hadoop .hbase .protobuf .ProtobufUtil ;
7274import org .apache .hadoop .hbase .util .EnvironmentEdgeManager ;
7375import org .apache .hadoop .hbase .util .HasThread ;
7476import org .apache .hadoop .hbase .util .IdReadWriteLock ;
@@ -242,6 +244,17 @@ public int compare(BlockCacheKey a, BlockCacheKey b) {
242244 /** In-memory bucket size */
243245 private float memoryFactor ;
244246
247+ private String ioEngineName ;
248+ private static final String FILE_VERIFY_ALGORITHM =
249+ "hbase.bucketcache.persistent.file.integrity.check.algorithm" ;
250+ private static final String DEFAULT_FILE_VERIFY_ALGORITHM = "MD5" ;
251+
252+ /**
253+ * Use {@link java.security.MessageDigest} class's encryption algorithms to check
254+ * persistence file integrity, default algorithm is MD5
255+ * */
256+ private String algorithm ;
257+
245258 public BucketCache (String ioEngineName , long capacity , int blockSize , int [] bucketSizes ,
246259 int writerThreadNum , int writerQLen , String persistencePath ) throws FileNotFoundException ,
247260 IOException {
@@ -252,8 +265,7 @@ public BucketCache(String ioEngineName, long capacity, int blockSize, int[] buck
252265 public BucketCache (String ioEngineName , long capacity , int blockSize , int [] bucketSizes ,
253266 int writerThreadNum , int writerQLen , String persistencePath , int ioErrorsTolerationDuration ,
254267 Configuration conf )
255- throws FileNotFoundException , IOException {
256- this .ioEngine = getIOEngineFromName (ioEngineName , capacity );
268+ throws IOException {
257269 this .writerThreads = new WriterThread [writerThreadNum ];
258270 long blockNumCapacity = capacity / blockSize ;
259271 if (blockNumCapacity >= Integer .MAX_VALUE ) {
@@ -275,6 +287,7 @@ public BucketCache(String ioEngineName, long capacity, int blockSize, int[] buck
275287 ", memoryFactor: " + memoryFactor );
276288
277289 this .cacheCapacity = capacity ;
290+ this .ioEngineName = ioEngineName ;
278291 this .persistencePath = persistencePath ;
279292 this .blockSize = blockSize ;
280293 this .ioErrorsTolerationDuration = ioErrorsTolerationDuration ;
@@ -288,14 +301,15 @@ public BucketCache(String ioEngineName, long capacity, int blockSize, int[] buck
288301 this .ramCache = new ConcurrentHashMap <BlockCacheKey , RAMQueueEntry >();
289302
290303 this .backingMap = new ConcurrentHashMap <BlockCacheKey , BucketEntry >((int ) blockNumCapacity );
291-
304+ this .algorithm = conf .get (FILE_VERIFY_ALGORITHM , DEFAULT_FILE_VERIFY_ALGORITHM );
305+ ioEngine = getIOEngineFromName ();
292306 if (ioEngine .isPersistent () && persistencePath != null ) {
293307 try {
294308 retrieveFromFile (bucketSizes );
295309 } catch (IOException ioex ) {
296310 LOG .error ("Can't restore from file because of" , ioex );
297311 } catch (ClassNotFoundException cnfe ) {
298- LOG .error ("Can't restore from file in rebuild because can't deserialise" ,cnfe );
312+ LOG .error ("Can't restore from file in rebuild because can't deserialise" , cnfe );
299313 throw new RuntimeException (cnfe );
300314 }
301315 }
@@ -359,24 +373,22 @@ public String getIoEngine() {
359373
360374 /**
361375 * Get the IOEngine from the IO engine name
362- * @param ioEngineName
363- * @param capacity
364376 * @return the IOEngine
365377 * @throws IOException
366378 */
367- private IOEngine getIOEngineFromName (String ioEngineName , long capacity )
379+ private IOEngine getIOEngineFromName ()
368380 throws IOException {
369381 if (ioEngineName .startsWith ("file:" ) || ioEngineName .startsWith ("files:" )) {
370382 // In order to make the usage simple, we only need the prefix 'files:' in
371383 // document whether one or multiple file(s), but also support 'file:' for
372384 // the compatibility
373385 String [] filePaths =
374386 ioEngineName .substring (ioEngineName .indexOf (":" ) + 1 ).split (FileIOEngine .FILE_DELIMITER );
375- return new FileIOEngine (capacity , filePaths );
387+ return new FileIOEngine (algorithm , persistencePath , cacheCapacity , filePaths );
376388 } else if (ioEngineName .startsWith ("offheap" ))
377- return new ByteBufferIOEngine (capacity , true );
389+ return new ByteBufferIOEngine (cacheCapacity , true );
378390 else if (ioEngineName .startsWith ("heap" ))
379- return new ByteBufferIOEngine (capacity , false );
391+ return new ByteBufferIOEngine (cacheCapacity , false );
380392 else
381393 throw new IllegalArgumentException (
382394 "Don't understand io engine name for cache - prefix with file:, heap or offheap" );
@@ -1021,41 +1033,48 @@ static List<RAMQueueEntry> getRAMQueueEntries(final BlockingQueue<RAMQueueEntry>
10211033
10221034 private void persistToFile () throws IOException {
10231035 assert !cacheEnabled ;
1024- FileOutputStream fos = null ;
1025- ObjectOutputStream oos = null ;
1026- try {
1036+ try (ObjectOutputStream oos = new ObjectOutputStream (
1037+ new FileOutputStream (persistencePath , false ))){
10271038 if (!ioEngine .isPersistent ()) {
10281039 throw new IOException ("Attempt to persist non-persistent cache mappings!" );
10291040 }
1030- fos = new FileOutputStream (persistencePath , false );
1031- oos = new ObjectOutputStream (fos );
1041+ if (ioEngine instanceof PersistentIOEngine ) {
1042+ oos .write (ProtobufUtil .PB_MAGIC );
1043+ byte [] checksum = ((PersistentIOEngine ) ioEngine ).calculateChecksum ();
1044+ oos .writeInt (checksum .length );
1045+ oos .write (checksum );
1046+ }
10321047 oos .writeLong (cacheCapacity );
10331048 oos .writeUTF (ioEngine .getClass ().getName ());
10341049 oos .writeUTF (backingMap .getClass ().getName ());
10351050 oos .writeObject (deserialiserMap );
10361051 oos .writeObject (backingMap );
1037- } finally {
1038- if (oos != null ) oos .close ();
1039- if (fos != null ) fos .close ();
1052+ } catch (NoSuchAlgorithmException e ) {
1053+ LOG .error ("No such algorithm : " + algorithm + "! Failed to persist data on exit" ,e );
10401054 }
10411055 }
10421056
10431057 @ SuppressWarnings ("unchecked" )
1044- private void retrieveFromFile (int [] bucketSizes ) throws IOException , BucketAllocatorException ,
1058+ private void retrieveFromFile (int [] bucketSizes ) throws IOException ,
10451059 ClassNotFoundException {
10461060 File persistenceFile = new File (persistencePath );
10471061 if (!persistenceFile .exists ()) {
10481062 return ;
10491063 }
10501064 assert !cacheEnabled ;
1051- FileInputStream fis = null ;
1052- ObjectInputStream ois = null ;
1053- try {
1065+ try (ObjectInputStream ois = new ObjectInputStream (new FileInputStream (persistencePath ))){
10541066 if (!ioEngine .isPersistent ())
10551067 throw new IOException (
10561068 "Attempt to restore non-persistent cache mappings!" );
1057- fis = new FileInputStream (persistencePath );
1058- ois = new ObjectInputStream (fis );
1069+ // for backward compatibility
1070+ if (ioEngine instanceof PersistentIOEngine &&
1071+ !((PersistentIOEngine ) ioEngine ).isOldVersion ()) {
1072+ byte [] PBMagic = new byte [ProtobufUtil .PB_MAGIC .length ];
1073+ ois .read (PBMagic );
1074+ int length = ois .readInt ();
1075+ byte [] persistenceChecksum = new byte [length ];
1076+ ois .read (persistenceChecksum );
1077+ }
10591078 long capacitySize = ois .readLong ();
10601079 if (capacitySize != cacheCapacity )
10611080 throw new IOException ("Mismatched cache capacity:"
@@ -1078,9 +1097,8 @@ private void retrieveFromFile(int[] bucketSizes) throws IOException, BucketAlloc
10781097 bucketAllocator = allocator ;
10791098 deserialiserMap = deserMap ;
10801099 backingMap = backingMapFromFile ;
1100+ blockNumber .set (backingMap .size ());
10811101 } finally {
1082- if (ois != null ) ois .close ();
1083- if (fis != null ) fis .close ();
10841102 if (!persistenceFile .delete ()) {
10851103 throw new IOException ("Failed deleting persistence file "
10861104 + persistenceFile .getAbsolutePath ());
@@ -1597,4 +1615,9 @@ float getMultiFactor() {
15971615 float getMemoryFactor () {
15981616 return memoryFactor ;
15991617 }
1618+
1619+ @ VisibleForTesting
1620+ public UniqueIndexMap <Integer > getDeserialiserMap () {
1621+ return deserialiserMap ;
1622+ }
16001623}
0 commit comments