/* * JBoss, Home of Professional Open Source * Copyright 2010 Red Hat Inc. and/or its affiliates and other * contributors as indicated by the @author tags. All rights reserved. * See the copyright.txt in the distribution for a full listing of * individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.infinispan.lucene; import java.util.Set; import org.infinispan.Cache; import org.infinispan.lucene.ChunkCacheKey; import org.infinispan.lucene.FileCacheKey; import org.infinispan.lucene.FileListCacheKey; import org.infinispan.lucene.FileMetadata; import org.testng.Assert; /** * DirectoryIntegrityCheck contains helpers to assert assumptions we make on the structure of an * index as stored in an Infinispan cache. * * @author Sanne Grinovero * @since 4.1 */ @SuppressWarnings("unchecked") public class DirectoryIntegrityCheck { /** * Verifies that no garbage elements are left over in the cache and that for each type of object * the expected value is stored. Also asserts for proper size metadata comparing to actual bytes * used in chunks. It's assumed that only one index is stored in the inspected cache, and that * the index is not being used by IndexReaders or IndexWriters. * * @param cache * The cache to inspect * @param indexName * The name of the unique index stored in the cache */ public static void verifyDirectoryStructure(Cache cache, String indexName) { verifyDirectoryStructure(cache, indexName, false); } public static void verifyDirectoryStructure(Cache cache, String indexName, boolean wasAStressTest) { Set<String> fileList = (Set<String>) cache.get(new FileListCacheKey(indexName)); Assert.assertNotNull(fileList); int fileListCacheKeyInstances = 0; for (Object key : cache.keySet()) { if (key instanceof ChunkCacheKey) { ChunkCacheKey existingChunkKey = (ChunkCacheKey) key; String filename = existingChunkKey.getFileName(); Assert.assertEquals(existingChunkKey.getIndexName(), indexName); // the chunk must either match an entry in fileList or have a pending readLock: // if (fileList.contains(filename) == false) { // verifyReadlockExists(cache, indexName, filename); // } Object value = cache.get(existingChunkKey); Assert.assertNotNull(value); Assert.assertTrue(value instanceof byte[]); byte[] buffer = (byte[]) cache.get(existingChunkKey); Assert.assertTrue(buffer.length != 0); } else if (key instanceof FileCacheKey) { FileCacheKey fileCacheKey = (FileCacheKey) key; Assert.assertEquals(fileCacheKey.getIndexName(), indexName); String filename = fileCacheKey.getFileName(); // if (fileList.contains(filename) == false) { // // if the file is not registered, assert that a readlock prevented it from being // // deleted: // verifyReadlockExists(cache, indexName, filename); // } Object value = cache.get(fileCacheKey); Assert.assertNotNull(value); Assert.assertTrue(value instanceof FileMetadata); FileMetadata metadata = (FileMetadata) value; long totalFileSize = metadata.getSize(); long actualFileSize = deepCountFileSize(fileCacheKey, cache); Assert.assertEquals(actualFileSize, totalFileSize); Assert.assertTrue(fileList.contains(fileCacheKey.getFileName()), fileCacheKey + " should not have existed"); } else if (key instanceof FileListCacheKey) { fileListCacheKeyInstances++; Assert.assertEquals(1, fileListCacheKeyInstances); } else if (key instanceof FileReadLockKey) { /*//FIXME testcase to be fixed after ISPN-616 FileReadLockKey readLockKey = (FileReadLockKey) key; Assert.assertEquals(readLockKey.getIndexName(), indexName); Object value = cache.get(readLockKey); // we verify that a ReadLock exists only for existing files Assert.assertTrue(cache.get(new FileCacheKey(indexName, readLockKey.getFileName())) != null, key + " left over from deleted " + readLockKey.getFileName()); Assert.assertTrue(cache.get(new ChunkCacheKey(indexName, readLockKey.getFileName(), 0)) != null); Assert.assertTrue(fileList.contains(readLockKey.getFileName()), "readlock still exists but the file was deleted: " + readLockKey); Assert.assertTrue(value == null || value.equals(1)); */ } else { Assert.fail("an unexpected key was found in the cache having key type " + key.getClass() + " toString:" + key); } } } private static void verifyReadlockExists(Cache cache, String indexName, String filename) { FileReadLockKey readLockKey = new FileReadLockKey(indexName, filename); Object readLockValue = cache.get(readLockKey); Assert.assertNotNull(readLockValue); Assert.assertTrue(readLockValue instanceof Integer); int v = (Integer) readLockValue; Assert.assertTrue(v > 1, "readlock exists for unregistered file of unexpected value: " + v + " for file: " + filename); } /** * For a given FileCacheKey return the total size of all chunks related to the file. * * @param fileCacheKey * the key to the file to inspect * @param cache * the cache storing the chunks * @return the total size adding all found chunks up */ public static long deepCountFileSize(FileCacheKey fileCacheKey, Cache cache) { String indexName = fileCacheKey.getIndexName(); String fileName = fileCacheKey.getFileName(); long accumulator = 0; for (int i = 0;; i++) { ChunkCacheKey chunkKey = new ChunkCacheKey(indexName, fileName, i); byte[] buffer = (byte[]) cache.get(chunkKey); if (buffer == null) { assert cache.containsKey(chunkKey)==false; return accumulator; } else { assert buffer.length > 0; //check we don't store useless data accumulator += buffer.length; } } } public static void assertFileNotExists(Cache cache, String indexName, String fileName, long maxWaitForCondition) throws InterruptedException { Set<String> fileList = (Set<String>) cache.get(new FileListCacheKey(indexName)); Assert.assertNotNull(fileList); Assert.assertFalse(fileList.contains(fileName)); //check is in sync: no waiting allowed in this case boolean allok = false; while (maxWaitForCondition >= 0 && !allok) { Thread.sleep(10); maxWaitForCondition -= 10; allok=true; if (cache.get(new FileCacheKey(indexName, fileName))!=null) allok=false; for (int i = 0; i < 100; i++) { ChunkCacheKey key = new ChunkCacheKey(indexName, fileName, i); if (cache.get(key)!=null) allok=false; } } Assert.assertTrue(allok); } /** * Verified the file exists and has a specified value for readLock; * Consider that null should be interpreted as value 1; */ public static void assertFileExistsHavingRLCount(Cache cache, String fileName, String indexName, int expectedReadcount, int chunkSize, boolean expectRegisteredInFat) { Set<String> fileList = (Set<String>) cache.get(new FileListCacheKey(indexName)); Assert.assertNotNull(fileList); Assert.assertTrue(fileList.contains(fileName) == expectRegisteredInFat); FileMetadata metadata = (FileMetadata) cache.get(new FileCacheKey(indexName, fileName)); Assert.assertNotNull(metadata); long totalFileSize = metadata.getSize(); int chunkNumbers = (int)(totalFileSize / chunkSize); for (int i = 0; i < chunkNumbers; i++) { Assert.assertNotNull(cache.get(new ChunkCacheKey(indexName, fileName, i))); } FileReadLockKey readLockKey = new FileReadLockKey(indexName,fileName); Object value = cache.get(readLockKey); if (expectedReadcount == 1) { Assert.assertTrue(value == null || Integer.valueOf(1).equals(value), "readlock value is " + value); } else { Assert.assertNotNull(value); Assert.assertTrue(value instanceof Integer); int v = (Integer)value; Assert.assertEquals(v, expectedReadcount); } } }