/** * Serializes a single SegmentChunk. * * @param segmentChunk The SegmentChunk to serialize. * @return A byte array containing the serialization. */ static byte[] serializeChunk(SegmentChunk segmentChunk) { return combine(Long.toString(segmentChunk.getStartOffset()), segmentChunk.getName()); }
/** * Sets the Active SegmentChunk handle. * * @param handle The handle. Must not be read-only and for the last SegmentChunk. */ synchronized void setActiveChunkHandle(SegmentHandle handle) { Preconditions.checkArgument(handle == null || !handle.isReadOnly(), "Active SegmentChunk handle cannot be readonly."); SegmentChunk last = lastChunk(); Preconditions.checkState(last != null, "Cannot set an Active SegmentChunk handle when there are no SegmentChunks."); Preconditions.checkArgument(handle == null || handle.getSegmentName().equals(last.getName()), "Active SegmentChunk handle must be for the last SegmentChunk."); this.activeChunkHandle = handle; }
/** * Adds a new SegmentChunk. * * @param segmentChunk The SegmentChunk to add. This SegmentChunk must be in continuity of any existing SegmentChunks. * @param activeChunkHandle The newly added SegmentChunk's write handle. */ synchronized void addChunk(SegmentChunk segmentChunk, SegmentHandle activeChunkHandle) { Preconditions.checkState(!this.sealed, "Cannot add SegmentChunks for a Sealed Handle."); if (this.segmentChunks.size() > 0) { long expectedOffset = this.segmentChunks.get(this.segmentChunks.size() - 1).getLastOffset(); Preconditions.checkArgument(segmentChunk.getStartOffset() == expectedOffset, "Invalid SegmentChunk StartOffset. Expected %s, given %s.", expectedOffset, segmentChunk.getStartOffset()); } // Update the SegmentChunk and its Handle atomically. Preconditions.checkNotNull(activeChunkHandle, "activeChunkHandle"); Preconditions.checkArgument(!activeChunkHandle.isReadOnly(), "Active SegmentChunk handle cannot be readonly."); Preconditions.checkArgument(activeChunkHandle.getSegmentName().equals(segmentChunk.getName()), "Active SegmentChunk handle must be for the last SegmentChunk."); this.activeChunkHandle = activeChunkHandle; this.segmentChunks.add(segmentChunk); }
private void createChunk(RollingSegmentHandle handle) throws StreamSegmentException { // Create new active SegmentChunk, only after which serialize the handle update and update the handle. // We ignore if the SegmentChunk exists and is empty - that's most likely due to a previous failed attempt. long segmentLength = handle.length(); SegmentChunk newSegmentChunk = SegmentChunk.forSegment(handle.getSegmentName(), segmentLength); try { this.baseStorage.create(newSegmentChunk.getName()); } catch (StreamSegmentExistsException ex) { checkIfEmptyAndNotSealed(ex, newSegmentChunk.getName()); } serializeNewChunk(handle, newSegmentChunk); val activeHandle = this.baseStorage.openWrite(newSegmentChunk.getName()); handle.addChunk(newSegmentChunk, activeHandle); log.debug("Created new SegmentChunk '{}' for '{}'.", newSegmentChunk, handle); }
private void refreshChunkExistence(RollingSegmentHandle handle) { // We check all SegmentChunks that we assume exist for actual existence (since once deleted, they can't come back). for (SegmentChunk s : handle.chunks()) { if (s.exists() && !this.baseStorage.exists(s.getName())) { s.markInexistent(); } } }
private void deleteChunks(RollingSegmentHandle handle, Predicate<SegmentChunk> canDelete) throws StreamSegmentException { for (SegmentChunk s : handle.chunks()) { if (s.exists() && canDelete.test(s)) { try { val subHandle = this.baseStorage.openWrite(s.getName()); this.baseStorage.delete(subHandle); s.markInexistent(); log.debug("Deleted SegmentChunk '{}' for '{}'.", s, handle); } catch (StreamSegmentNotExistsException ex) { // Ignore; It's OK if it doesn't exist; just make sure the handle is updated. s.markInexistent(); } } } }
private void unsealLastChunkIfNecessary(RollingSegmentHandle handle) throws StreamSegmentException { SegmentChunk last = handle.lastChunk(); if (last == null || !last.isSealed()) { // Nothing to do. return; } SegmentHandle activeChunk = handle.getActiveChunkHandle(); boolean needsHandleUpdate = activeChunk == null; if (needsHandleUpdate) { // We didn't have a pointer to the active chunk's Handle because the chunk was sealed before open-write. activeChunk = this.baseStorage.openWrite(last.getName()); } try { this.baseStorage.unseal(activeChunk); } catch (UnsupportedOperationException e) { log.warn("Unable to unseal SegmentChunk '{}' since base storage does not support unsealing.", last); return; } last.markUnsealed(); if (needsHandleUpdate) { activeChunk = this.baseStorage.openWrite(last.getName()); handle.setActiveChunkHandle(activeChunk); } log.debug("Unsealed active SegmentChunk '{}' for '{}'.", activeChunk.getSegmentName(), handle.getSegmentName()); }
@Override public SegmentHandle openWrite(String segmentName) throws StreamSegmentException { long traceId = LoggerHelpers.traceEnter(log, "openWrite", segmentName); val handle = openHandle(segmentName, false); // Finally, open the Active SegmentChunk for writing. SegmentChunk last = handle.lastChunk(); if (last != null && !last.isSealed()) { val activeHandle = this.baseStorage.openWrite(last.getName()); handle.setActiveChunkHandle(activeHandle); } LoggerHelpers.traceLeave(log, "openWrite", traceId, handle); return handle; }
val sh = this.baseStorage.openRead(current.getName()); int count = this.baseStorage.read(sh, readOffset, buffer, bufferOffset + bytesRead, readLength); bytesRead += count;
this.baseStorage.concat(target.getActiveChunkHandle(), target.lastChunk().getLength(), lastSource.getName()); target.lastChunk().increaseLength(lastSource.getLength()); if (source.getHeaderHandle() != null) {
String failOnDelete = writeHandle.chunks().get(failAtIndex).getName(); baseStorage.deleteFailure = sn -> sn.equals(failOnDelete) ? new IntentionalException() : null; AssertExtensions.assertThrows(
/** * Tests the ability of the Handle to refresh based on information from another similar handle. */ @Test public void testRefresh() { val headerHandle = new TestHandle(HEADER_NAME, true); val target = new RollingSegmentHandle(headerHandle, DEFAULT_ROLLING_POLICY, Collections.singletonList(new SegmentChunk("s1", 0L))); val source = new RollingSegmentHandle(headerHandle, DEFAULT_ROLLING_POLICY, Arrays.asList( new SegmentChunk("s1", 0L), new SegmentChunk("s2", 100L))); source.chunks().get(0).setLength(100); source.markSealed(); source.setHeaderLength(1000); source.setActiveChunkHandle(new TestHandle(source.lastChunk().getName(), false)); target.refresh(source); Assert.assertEquals("Unexpected getHeaderLength()", source.getHeaderLength(), target.getHeaderLength()); AssertExtensions.assertListEquals("Unexpected chunks()", source.chunks(), target.chunks(), Object::equals); Assert.assertTrue("Unexpected isSealed.", target.isSealed()); Assert.assertNull("Not expecting any ActiveSegmentHandle to be copied.", target.getActiveChunkHandle()); }
val si = this.baseStorage.getStreamSegmentInfo(last.getName()); last.setLength(si.getLength()); if (si.isSealed()) {
if (headerHandle == null) { val subHandle = this.baseStorage.openWrite(h.lastChunk().getName()); try { this.baseStorage.delete(subHandle);
boolean existsInStorage = baseStorage.exists(segmentChunk.getName()); Assert.assertEquals("Expected SegmentChunk deletion status for " + segmentChunk + ", truncation offset = " + truncateOffset, expectedExists, existsInStorage);