Search in sources :

Example 16 with BufferView

use of io.pravega.common.util.BufferView in project pravega by pravega.

the class DirectMemoryCacheTests method testCacheFull.

/**
 * Tests the ability to notify the caller that a cache is full and handle various situations.
 */
@Test
public void testCacheFull() {
    final int cleanAfterInvocations = DirectMemoryCache.MAX_CLEANUP_ATTEMPTS - 1;
    final int maxSize = LAYOUT.bufferSize();
    final int maxStoredSize = maxSize - LAYOUT.blockSize();
    final BufferView toInsert = new ByteArraySegment(new byte[1]);
    @Cleanup val c = new TestCache(maxSize);
    // Fill up the cache.
    val address1 = c.insert(new ByteArraySegment(new byte[maxStoredSize]));
    checkSnapshot(c, (long) maxStoredSize, (long) maxSize, (long) LAYOUT.blockSize(), (long) maxSize, (long) maxSize);
    // Return value from the setCacheFullCallback supplier.
    val reportClean = new AtomicBoolean(false);
    // If true, will actually perform a cleanup.
    val actualClean = new AtomicBoolean(false);
    // If true, will throw an error.
    val throwError = new AtomicBoolean(false);
    val invocationCount = new AtomicInteger(0);
    c.setCacheFullCallback(() -> {
        invocationCount.incrementAndGet();
        if (actualClean.get() && invocationCount.get() >= cleanAfterInvocations) {
            c.delete(address1);
        }
        if (throwError.get()) {
            throw new IntentionalException();
        }
        return reportClean.get();
    }, 1);
    // 1. No cleanup, no error, reporting the truth. We should try as many times as possible.
    reportClean.set(false);
    actualClean.set(false);
    throwError.set(false);
    invocationCount.set(0);
    AssertExtensions.assertThrows("Expected CacheFullException when no cleanup and no error.", () -> c.insert(toInsert), ex -> ex instanceof CacheFullException);
    Assert.assertEquals("Unexpected number of invocations when no cleanup and no error.", DirectMemoryCache.MAX_CLEANUP_ATTEMPTS, invocationCount.get());
    // 2. No cleanup, no error, reporting that we did clean up. We should try as many times as possible.
    reportClean.set(true);
    actualClean.set(false);
    throwError.set(false);
    invocationCount.set(0);
    AssertExtensions.assertThrows("Expected CacheFullException when no cleanup and no error, but reporting clean.", () -> c.insert(toInsert), ex -> ex instanceof CacheFullException);
    Assert.assertEquals("Unexpected number of invocations when no cleanup but reporting clean.", DirectMemoryCache.MAX_CLEANUP_ATTEMPTS, invocationCount.get());
    // 3. No cleanup, throw error.
    reportClean.set(false);
    actualClean.set(false);
    throwError.set(true);
    invocationCount.set(0);
    AssertExtensions.assertThrows("Expected IntentionalException when error.", () -> c.insert(toInsert), ex -> ex instanceof IntentionalException);
    Assert.assertEquals("Unexpected number of invocations when error.", 1, invocationCount.get());
    // 4. Cleanup.
    reportClean.set(true);
    actualClean.set(true);
    throwError.set(false);
    invocationCount.set(0);
    val address2 = c.insert(toInsert);
    Assert.assertEquals("Unexpected number of invocations when successful cleanup.", cleanAfterInvocations, invocationCount.get());
    Assert.assertEquals("Unexpected second entry.", 1, c.get(address2).getLength());
    checkSnapshot(c, 1L, LAYOUT.blockSize() * 2L, (long) LAYOUT.blockSize(), (long) maxSize, (long) maxSize);
}
Also used : lombok.val(lombok.val) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) ByteArraySegment(io.pravega.common.util.ByteArraySegment) BufferView(io.pravega.common.util.BufferView) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Cleanup(lombok.Cleanup) IntentionalException(io.pravega.test.common.IntentionalException) Test(org.junit.Test)

Example 17 with BufferView

use of io.pravega.common.util.BufferView in project pravega by pravega.

the class TableBasedMetadataStore method read.

/**
 * Reads a metadata record for the given key.
 *
 * @param key Key for the metadata record.
 * @return Associated {@link io.pravega.segmentstore.storage.metadata.BaseMetadataStore.TransactionData}.
 */
@Override
protected CompletableFuture<TransactionData> read(String key) {
    val keys = new ArrayList<BufferView>();
    keys.add(new ByteArraySegment(key.getBytes(Charsets.UTF_8)));
    val t = new Timer();
    return ensureInitialized().thenComposeAsync(v -> this.tableStore.get(tableName, keys, timeout).thenApplyAsync(entries -> {
        try {
            Preconditions.checkState(entries.size() == 1, "Unexpected number of values returned.");
            val entry = entries.get(0);
            if (null != entry) {
                val arr = entry.getValue();
                TransactionData txnData = SERIALIZER.deserialize(arr);
                txnData.setDbObject(entry.getKey().getVersion());
                txnData.setPersisted(true);
                TABLE_GET_LATENCY.reportSuccessEvent(t.getElapsed());
                METADATA_FOUND_IN_STORE.inc();
                return txnData;
            }
        } catch (Exception e) {
            throw new CompletionException(new StorageMetadataException("Error while reading", e));
        }
        TABLE_GET_LATENCY.reportSuccessEvent(t.getElapsed());
        METADATA_NOT_FOUND.inc();
        return TransactionData.builder().key(key).persisted(true).dbObject(TableKey.NOT_EXISTS).build();
    }, getExecutor()).exceptionally(e -> {
        val ex = Exceptions.unwrap(e);
        throw new CompletionException(handleException(ex));
    }), getExecutor());
}
Also used : lombok.val(lombok.val) Getter(lombok.Getter) TableStore(io.pravega.segmentstore.contracts.tables.TableStore) Spliterators(java.util.Spliterators) Exceptions(io.pravega.common.Exceptions) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) ChunkedSegmentStorageConfig(io.pravega.segmentstore.storage.chunklayer.ChunkedSegmentStorageConfig) TABLE_WRITE_LATENCY(io.pravega.segmentstore.storage.metadata.StorageMetadataMetrics.TABLE_WRITE_LATENCY) BadKeyVersionException(io.pravega.segmentstore.contracts.tables.BadKeyVersionException) IteratorArgs(io.pravega.segmentstore.contracts.tables.IteratorArgs) ArrayList(java.util.ArrayList) SegmentType(io.pravega.segmentstore.contracts.SegmentType) IteratorItem(io.pravega.segmentstore.contracts.tables.IteratorItem) BufferView(io.pravega.common.util.BufferView) Duration(java.time.Duration) StreamSupport(java.util.stream.StreamSupport) METADATA_FOUND_IN_STORE(io.pravega.segmentstore.storage.metadata.StorageMetadataMetrics.METADATA_FOUND_IN_STORE) METADATA_NOT_FOUND(io.pravega.segmentstore.storage.metadata.StorageMetadataMetrics.METADATA_NOT_FOUND) Charsets(com.google.common.base.Charsets) TABLE_GET_LATENCY(io.pravega.segmentstore.storage.metadata.StorageMetadataMetrics.TABLE_GET_LATENCY) TableKey(io.pravega.segmentstore.contracts.tables.TableKey) Executor(java.util.concurrent.Executor) Collection(java.util.Collection) lombok.val(lombok.val) AsyncIterator(io.pravega.common.util.AsyncIterator) CompletionException(java.util.concurrent.CompletionException) DataLogWriterNotPrimaryException(io.pravega.segmentstore.storage.DataLogWriterNotPrimaryException) Timer(io.pravega.common.Timer) Slf4j(lombok.extern.slf4j.Slf4j) Stream(java.util.stream.Stream) ByteArraySegment(io.pravega.common.util.ByteArraySegment) StreamSegmentExistsException(io.pravega.segmentstore.contracts.StreamSegmentExistsException) Preconditions(com.google.common.base.Preconditions) VisibleForTesting(com.google.common.annotations.VisibleForTesting) TableEntry(io.pravega.segmentstore.contracts.tables.TableEntry) Spliterator(java.util.Spliterator) ByteArraySegment(io.pravega.common.util.ByteArraySegment) Timer(io.pravega.common.Timer) CompletionException(java.util.concurrent.CompletionException) ArrayList(java.util.ArrayList) BadKeyVersionException(io.pravega.segmentstore.contracts.tables.BadKeyVersionException) CompletionException(java.util.concurrent.CompletionException) DataLogWriterNotPrimaryException(io.pravega.segmentstore.storage.DataLogWriterNotPrimaryException) StreamSegmentExistsException(io.pravega.segmentstore.contracts.StreamSegmentExistsException)

Example 18 with BufferView

use of io.pravega.common.util.BufferView in project pravega by pravega.

the class BenchmarkTests method testRandomOperations.

private RandomResult testRandomOperations(CacheStorage s) {
    val writeBuffer = new ByteArraySegment(new byte[ENTRY_SIZE]);
    this.random.nextBytes(writeBuffer.array());
    val ids = new ArrayList<Integer>();
    val timer = new Timer();
    val threads = new ArrayList<Thread>();
    val iterations = new AtomicInteger(0);
    val insertCount = new AtomicInteger(0);
    val getCount = new AtomicInteger(0);
    val deleteCount = new AtomicInteger(0);
    for (int threadId = 0; threadId < RANDOM_OPERATIONS_THREAD_COUNT; threadId++) {
        val t = new Thread(() -> {
            val readBuffer = new byte[ENTRY_SIZE * 2];
            int i;
            while ((i = iterations.incrementAndGet()) <= ENTRY_COUNT) {
                boolean insert;
                int length;
                synchronized (ids) {
                    insert = (this.random.nextInt(100) < RANDOM_OPERATIONS_INSERT_PERCENTAGE) || ids.isEmpty();
                    length = insert ? this.random.nextInt(writeBuffer.getLength()) : 0;
                }
                if (insert) {
                    int insertedId = s.insert(writeBuffer.slice(0, length));
                    synchronized (ids) {
                        ids.add(insertedId);
                    }
                    insertCount.incrementAndGet();
                } else {
                    // delete
                    int toRemove;
                    synchronized (ids) {
                        toRemove = ids.remove(this.random.nextInt(ids.size()));
                    }
                    s.delete(toRemove);
                    deleteCount.incrementAndGet();
                }
                int toRead = -1;
                synchronized (ids) {
                    if (!ids.isEmpty()) {
                        toRead = ids.get(this.random.nextInt(ids.size()));
                    }
                }
                if (toRead >= 0) {
                    BufferView result = s.get(toRead);
                    if (result != null) {
                        result.copyTo(ByteBuffer.wrap(readBuffer));
                    }
                    getCount.incrementAndGet();
                }
            }
        });
        t.start();
        threads.add(t);
    }
    for (val t : threads) {
        Exceptions.handleInterrupted(t::join);
    }
    Duration elapsed = timer.getElapsed();
    // do not count this.
    ids.forEach(s::delete);
    return new RandomResult(elapsed, insertCount.get(), getCount.get(), deleteCount.get());
}
Also used : lombok.val(lombok.val) ByteArraySegment(io.pravega.common.util.ByteArraySegment) ArrayList(java.util.ArrayList) Duration(java.time.Duration) Exceptions(io.pravega.common.Exceptions) Timer(io.pravega.common.Timer) BufferView(io.pravega.common.util.BufferView) Test(org.junit.Test) ArrayList(java.util.ArrayList) ByteArraySegment(io.pravega.common.util.ByteArraySegment) AtomicInteger(java.util.concurrent.atomic.AtomicInteger)

Example 19 with BufferView

use of io.pravega.common.util.BufferView in project pravega by pravega.

the class StreamSegmentStoreTestBase method checkReadsWhileTruncating.

private void checkReadsWhileTruncating(HashMap<String, ByteArrayOutputStream> segmentContents, HashMap<String, Long> startOffsets, StreamSegmentStore store) throws Exception {
    for (Map.Entry<String, ByteArrayOutputStream> e : segmentContents.entrySet()) {
        String segmentName = e.getKey();
        byte[] expectedData = e.getValue().toByteArray();
        long segmentLength = store.getStreamSegmentInfo(segmentName, TIMEOUT).join().getLength();
        long expectedCurrentOffset = 0;
        boolean truncate = false;
        while (expectedCurrentOffset < segmentLength) {
            @Cleanup ReadResult readResult = store.read(segmentName, expectedCurrentOffset, (int) (segmentLength - expectedCurrentOffset), TIMEOUT).join();
            Assert.assertTrue("Empty read result for segment " + segmentName, readResult.hasNext());
            // We only test the truncation-related pieces here; other read-related checks are done in checkReads.
            while (readResult.hasNext()) {
                ReadResultEntry readEntry = readResult.next();
                Assert.assertEquals("Unexpected value from getStreamSegmentOffset for segment " + segmentName, expectedCurrentOffset, readEntry.getStreamSegmentOffset());
                if (!readEntry.getContent().isDone()) {
                    readEntry.requestContent(TIMEOUT);
                }
                if (readEntry.getType() == ReadResultEntryType.Truncated) {
                    long startOffset = startOffsets.getOrDefault(segmentName, 0L);
                    // Verify that the Segment actually is truncated beyond this offset.
                    AssertExtensions.assertLessThan("Found Truncated ReadResultEntry but current offset not truncated.", startOffset, readEntry.getStreamSegmentOffset());
                    // Verify the ReadResultEntry cannot be used and throws an appropriate exception.
                    AssertExtensions.assertSuppliedFutureThrows("ReadEntry.getContent() did not throw for a Truncated entry.", readEntry::getContent, ex -> ex instanceof StreamSegmentTruncatedException);
                    // Verify ReadResult is done.
                    Assert.assertFalse("Unexpected result from ReadResult.hasNext when encountering truncated entry.", readResult.hasNext());
                    // Verify attempting to read at the current offset will return the appropriate entry (and not throw).
                    @Cleanup ReadResult truncatedResult = store.read(segmentName, readEntry.getStreamSegmentOffset(), 1, TIMEOUT).join();
                    val first = truncatedResult.next();
                    Assert.assertEquals("Read request for a truncated offset did not start with a Truncated ReadResultEntryType.", ReadResultEntryType.Truncated, first.getType());
                    // Skip over until the first non-truncated offset.
                    expectedCurrentOffset = Math.max(expectedCurrentOffset, startOffset);
                    continue;
                }
                // Non-truncated entry; do the usual verifications.
                readEntry.getContent().get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
                Assert.assertNotEquals("Unexpected value for isEndOfStreamSegment for non-sealed segment " + segmentName, ReadResultEntryType.EndOfStreamSegment, readEntry.getType());
                BufferView readEntryContents = readEntry.getContent().join();
                byte[] actualData = readEntryContents.getCopy();
                AssertExtensions.assertArrayEquals("Unexpected data read from segment " + segmentName + " at offset " + expectedCurrentOffset, expectedData, (int) expectedCurrentOffset, actualData, 0, readEntryContents.getLength());
                expectedCurrentOffset += readEntryContents.getLength();
                // Every other read, determine if we should truncate or not.
                if (truncate) {
                    long truncateOffset;
                    if (segmentName.hashCode() % 2 == 0) {
                        // Truncate just beyond the current read offset.
                        truncateOffset = Math.min(segmentLength, expectedCurrentOffset + 1);
                    } else {
                        // Truncate half of what we read so far.
                        truncateOffset = Math.min(segmentLength, expectedCurrentOffset / 2 + 1);
                    }
                    startOffsets.put(segmentName, truncateOffset);
                    store.truncateStreamSegment(segmentName, truncateOffset, TIMEOUT).join();
                }
                truncate = !truncate;
            }
            Assert.assertTrue("ReadResult was not closed post-full-consumption for segment" + segmentName, readResult.isClosed());
        }
    }
}
Also used : lombok.val(lombok.val) ReadResult(io.pravega.segmentstore.contracts.ReadResult) ByteArrayOutputStream(java.io.ByteArrayOutputStream) StreamSegmentTruncatedException(io.pravega.segmentstore.contracts.StreamSegmentTruncatedException) Cleanup(lombok.Cleanup) BufferView(io.pravega.common.util.BufferView) ReadResultEntry(io.pravega.segmentstore.contracts.ReadResultEntry) Map(java.util.Map) HashMap(java.util.HashMap)

Example 20 with BufferView

use of io.pravega.common.util.BufferView in project pravega by pravega.

the class StreamSegmentStoreTestBase method checkSegmentReads.

private void checkSegmentReads(String segmentName, AtomicLong expectedCurrentOffset, long segmentLength, StreamSegmentStore store, byte[] expectedData) throws Exception {
    @Cleanup ReadResult readResult = store.read(segmentName, expectedCurrentOffset.get(), (int) (segmentLength - expectedCurrentOffset.get()), TIMEOUT).join();
    Assert.assertTrue("Empty read result for segment " + segmentName, readResult.hasNext());
    // A more thorough read check is done in StreamSegmentContainerTests; here we just check if the data was merged correctly.
    while (readResult.hasNext()) {
        ReadResultEntry readEntry = readResult.next();
        AssertExtensions.assertGreaterThan("getRequestedReadLength should be a positive integer for segment " + segmentName, 0, readEntry.getRequestedReadLength());
        Assert.assertEquals("Unexpected value from getStreamSegmentOffset for segment " + segmentName, expectedCurrentOffset.get(), readEntry.getStreamSegmentOffset());
        if (!readEntry.getContent().isDone()) {
            readEntry.requestContent(TIMEOUT);
        }
        readEntry.getContent().get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
        Assert.assertNotEquals("Unexpected value for isEndOfStreamSegment for non-sealed segment " + segmentName, ReadResultEntryType.EndOfStreamSegment, readEntry.getType());
        BufferView readEntryContents = readEntry.getContent().join();
        byte[] actualData = readEntryContents.getCopy();
        AssertExtensions.assertArrayEquals("Unexpected data read from segment " + segmentName + " at offset " + expectedCurrentOffset, expectedData, (int) expectedCurrentOffset.get(), actualData, 0, readEntryContents.getLength());
        expectedCurrentOffset.addAndGet(readEntryContents.getLength());
    }
    Assert.assertTrue("ReadResult was not closed post-full-consumption for segment" + segmentName, readResult.isClosed());
}
Also used : BufferView(io.pravega.common.util.BufferView) ReadResultEntry(io.pravega.segmentstore.contracts.ReadResultEntry) ReadResult(io.pravega.segmentstore.contracts.ReadResult) Cleanup(lombok.Cleanup)

Aggregations

BufferView (io.pravega.common.util.BufferView)77 ArrayList (java.util.ArrayList)49 lombok.val (lombok.val)49 ByteArraySegment (io.pravega.common.util.ByteArraySegment)44 Cleanup (lombok.Cleanup)42 Duration (java.time.Duration)39 Test (org.junit.Test)39 List (java.util.List)37 CompletableFuture (java.util.concurrent.CompletableFuture)34 AssertExtensions (io.pravega.test.common.AssertExtensions)29 HashMap (java.util.HashMap)29 Assert (org.junit.Assert)29 ThreadPooledTestSuite (io.pravega.test.common.ThreadPooledTestSuite)28 TimeUnit (java.util.concurrent.TimeUnit)28 AtomicReference (java.util.concurrent.atomic.AtomicReference)26 Collectors (java.util.stream.Collectors)26 AtomicLong (java.util.concurrent.atomic.AtomicLong)25 Exceptions (io.pravega.common.Exceptions)24 TableEntry (io.pravega.segmentstore.contracts.tables.TableEntry)24 Map (java.util.Map)22