/** * Construct the ByteBufferIOEngine with the given capacity * @param capacity * @throws IOException ideally here no exception to be thrown from the allocator */ public ByteBufferIOEngine(long capacity) throws IOException { this.capacity = capacity; ByteBufferAllocator allocator = new ByteBufferAllocator() { @Override public ByteBuffer allocate(long size) throws IOException { return ByteBuffer.allocateDirect((int) size); } }; bufferArray = new ByteBufferArray(capacity, allocator); }
/** * We allocate a number of byte buffers as the capacity. In order not to out * of the array bounds for the last byte(see {@link ByteBufferArray#multiple}), * we will allocate one additional buffer with capacity 0; * @param capacity total size of the byte buffer array * @param allocator the ByteBufferAllocator that will create the buffers * @throws IOException throws IOException if there is an exception thrown by the allocator */ public ByteBufferArray(long capacity, ByteBufferAllocator allocator) throws IOException { this.bufferSize = DEFAULT_BUFFER_SIZE; if (this.bufferSize > (capacity / 16)) this.bufferSize = (int) roundUp(capacity / 16, 32768); this.bufferCount = (int) (roundUp(capacity, bufferSize) / bufferSize); LOG.info("Allocating buffers total=" + StringUtils.byteDesc(capacity) + ", sizePerBuffer=" + StringUtils.byteDesc(bufferSize) + ", count=" + bufferCount); buffers = new ByteBuffer[bufferCount + 1]; createBuffers(allocator); }
/** * Transfers bytes from this buffer array into the given destination array * @param start start position in the ByteBufferArray * @param len The maximum number of bytes to be written to the given array * @param dstArray The array into which bytes are to be written * @return number of bytes read */ public int getMultiple(long start, int len, byte[] dstArray) { return getMultiple(start, len, dstArray, 0); }
@Test public void testAsSubBufferWhenEndOffsetLandInLastBuffer() throws Exception { int capacity = 4 * 1024 * 1024; ByteBufferAllocator allocator = new ByteBufferAllocator() { @Override public ByteBuffer allocate(long size) throws IOException { return ByteBuffer.allocateDirect((int) size); } }; ByteBufferArray array = new ByteBufferArray(capacity, allocator); ByteBuff subBuf = array.asSubByteBuff(0, capacity); subBuf.position(capacity - 1);// Position to the last byte assertTrue(subBuf.hasRemaining()); // Read last byte subBuf.get(); assertFalse(subBuf.hasRemaining()); }
/** * Transfers bytes from the given source array into this buffer array * @param start start offset of this buffer array * @param len The maximum number of bytes to be read from the given array * @param srcArray The array from which bytes are to be read */ public void putMultiple(long start, int len, byte[] srcArray) { putMultiple(start, len, srcArray, 0); }
/** * Transfers bytes from the given source array into this buffer array * @param start start offset of this buffer array * @param len The maximum number of bytes to be read from the given array * @param srcArray The array from which bytes are to be read * @param srcOffset The offset within the given array of the first byte to be * read */ public void putMultiple(long start, int len, byte[] srcArray, int srcOffset) { multiple(start, len, srcArray, srcOffset, PUT_MULTIPLE_VISITOR); }
@Test public void testByteBufferCreation1() throws Exception { ByteBufferAllocator allocator = new ByteBufferAllocator() { @Override public ByteBuffer allocate(long size) throws IOException { return ByteBuffer.allocateDirect((int) size); } }; ByteBufferArray array = new DummyByteBufferArray(7 * 1024 * 1024, allocator); // overwrite array.bufferCount = 25; array.buffers = new ByteBuffer[array.bufferCount + 1]; array.createBuffers(allocator); for (int i = 0; i < array.buffers.length; i++) { if (i == array.buffers.length - 1) { assertEquals(0, array.buffers[i].capacity()); } else { assertEquals(458752, array.buffers[i].capacity()); } } }
@Override public Cacheable read(long offset, int length, CacheableDeserializer<Cacheable> deserializer) throws IOException { ByteBuff dstBuffer = bufferArray.asSubByteBuff(offset, length); // Here the buffer that is created directly refers to the buffer in the actual buckets. // When any cell is referring to the blocks created out of these buckets then it means that // those cells are referring to a shared memory area which if evicted by the BucketCache would // lead to corruption of results. Hence we set the type of the buffer as SHARED_MEMORY // so that the readers using this block are aware of this fact and do the necessary action // to prevent eviction till the results are either consumed or copied return deserializer.deserialize(dstBuffer, true, MemoryType.SHARED); }
@VisibleForTesting void createBuffers(ByteBufferAllocator allocator) throws IOException { int threadCount = getThreadCount(); ExecutorService service = new ThreadPoolExecutor(threadCount, threadCount, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
/** * We allocate a number of byte buffers as the capacity. In order not to out * of the array bounds for the last byte(see {@link ByteBufferArray#multiple}), * we will allocate one additional buffer with capacity 0; * @param capacity total size of the byte buffer array * @param directByteBuffer true if we allocate direct buffer */ public ByteBufferArray(long capacity, boolean directByteBuffer) { this.bufferSize = DEFAULT_BUFFER_SIZE; if (this.bufferSize > (capacity / 16)) this.bufferSize = (int) roundUp(capacity / 16, 32768); this.bufferCount = (int) (roundUp(capacity, bufferSize) / bufferSize); LOG.info("Allocating buffers total=" + StringUtils.byteDesc(capacity) + ", sizePerBuffer=" + StringUtils.byteDesc(bufferSize) + ", count=" + bufferCount + ", direct=" + directByteBuffer); buffers = new ByteBuffer[bufferCount + 1]; locks = new Lock[bufferCount + 1]; for (int i = 0; i <= bufferCount; i++) { locks[i] = new ReentrantLock(); if (i < bufferCount) { buffers[i] = directByteBuffer ? ByteBuffer.allocateDirect(bufferSize) : ByteBuffer.allocate(bufferSize); } else { buffers[i] = ByteBuffer.allocate(0); } } }
@Test public void testAsSubBufferWhenEndOffsetLandInLastBuffer() throws Exception { int capacity = 4 * 1024 * 1024; ByteBufferAllocator allocator = new ByteBufferAllocator() { @Override public ByteBuffer allocate(long size) throws IOException { return ByteBuffer.allocateDirect((int) size); } }; ByteBufferArray array = new ByteBufferArray(capacity, allocator); ByteBuff subBuf = array.asSubByteBuff(0, capacity); subBuf.position(capacity - 1);// Position to the last byte assertTrue(subBuf.hasRemaining()); // Read last byte subBuf.get(); assertFalse(subBuf.hasRemaining()); }
/** * Transfers data from the given byte buffer to the buffer array * @param srcBuffer the given byte buffer from which bytes are to be read * @param offset The offset in the ByteBufferArray of the first byte to be * written * @throws IOException throws IOException if writing to the array throws exception */ @Override public void write(ByteBuffer srcBuffer, long offset) throws IOException { assert srcBuffer.hasArray(); bufferArray.putMultiple(offset, srcBuffer.remaining(), srcBuffer.array(), srcBuffer.arrayOffset()); }
/** * Transfers bytes from this buffer array into the given destination array * @param start start offset of this buffer array * @param len The maximum number of bytes to be written to the given array * @param dstArray The array into which bytes are to be written * @param dstOffset The offset within the given array of the first byte to be * written * @return number of bytes read */ public int getMultiple(long start, int len, byte[] dstArray, int dstOffset) { multiple(start, len, dstArray, dstOffset, GET_MULTIPLE_VISTOR); return len; }
@Test public void testByteBufferCreation1() throws Exception { ByteBufferAllocator allocator = new ByteBufferAllocator() { @Override public ByteBuffer allocate(long size) throws IOException { return ByteBuffer.allocateDirect((int) size); } }; ByteBufferArray array = new DummyByteBufferArray(7 * 1024 * 1024, allocator); // overwrite array.bufferCount = 25; array.buffers = new ByteBuffer[array.bufferCount + 1]; array.createBuffers(allocator); for (int i = 0; i < array.buffers.length; i++) { if (i == array.buffers.length - 1) { assertEquals(0, array.buffers[i].capacity()); } else { assertEquals(458752, array.buffers[i].capacity()); } } }
@VisibleForTesting void createBuffers(ByteBufferAllocator allocator) throws IOException { int threadCount = getThreadCount(); ExecutorService service = new ThreadPoolExecutor(threadCount, threadCount, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
@Test public void testAsSubBufferWhenEndOffsetLandInLastBuffer() throws Exception { int capacity = 4 * 1024 * 1024; ByteBufferAllocator allocator = new ByteBufferAllocator() { @Override public ByteBuffer allocate(long size) throws IOException { return ByteBuffer.allocateDirect((int) size); } }; ByteBufferArray array = new ByteBufferArray(capacity, allocator); ByteBuff subBuf = array.asSubByteBuff(0, capacity); subBuf.position(capacity - 1);// Position to the last byte assertTrue(subBuf.hasRemaining()); // Read last byte subBuf.get(); assertFalse(subBuf.hasRemaining()); }
/** * We allocate a number of byte buffers as the capacity. In order not to out * of the array bounds for the last byte(see {@link ByteBufferArray#multiple}), * we will allocate one additional buffer with capacity 0; * @param capacity total size of the byte buffer array * @param allocator the ByteBufferAllocator that will create the buffers * @throws IOException throws IOException if there is an exception thrown by the allocator */ public ByteBufferArray(long capacity, ByteBufferAllocator allocator) throws IOException { this.bufferSize = DEFAULT_BUFFER_SIZE; if (this.bufferSize > (capacity / 16)) this.bufferSize = (int) roundUp(capacity / 16, 32768); this.bufferCount = (int) (roundUp(capacity, bufferSize) / bufferSize); LOG.info("Allocating buffers total=" + StringUtils.byteDesc(capacity) + ", sizePerBuffer=" + StringUtils.byteDesc(bufferSize) + ", count=" + bufferCount); buffers = new ByteBuffer[bufferCount + 1]; createBuffers(allocator); }
/** * Transfers data from the given byte buffer to file * @param srcBuffer the given byte buffer from which bytes are to be read * @param offset The offset in the file where the first byte to be written * @throws IOException */ @Override public void write(ByteBuffer srcBuffer, long offset) throws IOException { assert srcBuffer.hasArray(); bufferArray.putMultiple(offset, srcBuffer.remaining(), srcBuffer.array(), srcBuffer.arrayOffset()); }
@Override public Cacheable read(long offset, int length, CacheableDeserializer<Cacheable> deserializer) throws IOException { byte[] dst = new byte[length]; bufferArray.getMultiple(offset, length, dst); return deserializer.deserialize(new SingleByteBuff(ByteBuffer.wrap(dst)), true, MemoryType.EXCLUSIVE); }
public FileMmapEngine(String filePath, long capacity) throws IOException { this.path = filePath; this.size = capacity; long fileSize = 0; try { raf = new RandomAccessFile(filePath, "rw"); fileSize = roundUp(capacity, ByteBufferArray.DEFAULT_BUFFER_SIZE); raf.setLength(fileSize); fileChannel = raf.getChannel(); LOG.info("Allocating " + StringUtils.byteDesc(fileSize) + ", on the path:" + filePath); } catch (java.io.FileNotFoundException fex) { LOG.error("Can't create bucket cache file " + filePath, fex); throw fex; } catch (IOException ioex) { LOG.error("Can't extend bucket cache file; insufficient space for " + StringUtils.byteDesc(fileSize), ioex); shutdown(); throw ioex; } ByteBufferAllocator allocator = new ByteBufferAllocator() { AtomicInteger pos = new AtomicInteger(0); @Override public ByteBuffer allocate(long size) throws IOException { ByteBuffer buffer = fileChannel.map(java.nio.channels.FileChannel.MapMode.READ_WRITE, pos.getAndIncrement() * size, size); return buffer; } }; bufferArray = new ByteBufferArray(fileSize, allocator); }