Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blobstore can throw FileAlreadyExistsException #10447 #10831

Merged
merged 1 commit into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import java.io.IOException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -172,11 +172,14 @@
if ( !Files.exists( file ) )
{
Files.createDirectories( file.getParent() );
in.copyTo( MoreFiles.asByteSink( file ) );
}
else
{
Files.setLastModifiedTime( file, FileTime.fromMillis( System.currentTimeMillis() ) );
try (var inStream = in.openStream())
{
Files.copy( inStream, file );
}
catch ( FileAlreadyExistsException e )

Check warning on line 179 in modules/blobstore/blobstore-file/src/main/java/com/enonic/xp/internal/blobstore/file/FileBlobStore.java

View check run for this annotation

Codecov / codecov/patch

modules/blobstore/blobstore-file/src/main/java/com/enonic/xp/internal/blobstore/file/FileBlobStore.java#L179

Added line #L179 was not covered by tests
{
LOG.debug( "File already exists [{}]", file, e );

Check warning on line 181 in modules/blobstore/blobstore-file/src/main/java/com/enonic/xp/internal/blobstore/file/FileBlobStore.java

View check run for this annotation

Codecov / codecov/patch

modules/blobstore/blobstore-file/src/main/java/com/enonic/xp/internal/blobstore/file/FileBlobStore.java#L181

Added line #L181 was not covered by tests
}
}

return new FileBlobRecord( key, file );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class FileBlobStoreTest
{
Expand Down Expand Up @@ -120,18 +119,6 @@ public void deleteSegment()
assertEquals( segment, blobStore.listSegments().findFirst().get() );
}

@Test
public void lastModifiedUpdated()
throws Exception
{
final BlobRecord rec1 = this.blobStore.addRecord( this.segment, ByteSource.wrap( "hello".getBytes() ) );
final long beforeUpdate = rec1.lastModified();
Thread.sleep( 1000 ); // ensure a second difference
final BlobRecord rec2 = this.blobStore.addRecord( this.segment, ByteSource.wrap( "hello".getBytes() ) );

assertTrue( beforeUpdate < rec2.lastModified() );
}

private BlobRecord createRecord( final String str )
{
return createRecord( segment, str );
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.enonic.xp.internal.blobstore.cache;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Objects;
import java.util.stream.Stream;

import org.slf4j.Logger;
Expand Down Expand Up @@ -76,7 +78,6 @@
throws BlobStoreException
{
this.store.removeRecord( segment, key );
this.cache.invalidate( key );
}

@Override
Expand All @@ -87,21 +88,32 @@

private BlobRecord addToCache( final BlobRecord record )
{
if ( record.getLength() <= this.sizeThreshold )
// Quick check to avoid caching large blobs
if ( record.getLength() > this.sizeThreshold )
{
try
{
final CacheBlobRecord cacheBlobRecord = new CacheBlobRecord( record );
this.cache.put( record.getKey(), cacheBlobRecord );
return cacheBlobRecord;
}
catch ( IOException e )
{
LOG.error( "Could not create cache blob-record", e );
}
return record;
}

// Don't do heavy lifting if the blob is already in the cache
final BlobKey key = record.getKey();
final CacheBlobRecord present = this.cache.getIfPresent( key );
if ( present != null )
{
return present;

Check warning on line 102 in modules/core/core-blobstore/src/main/java/com/enonic/xp/internal/blobstore/cache/CachedBlobStore.java

View check run for this annotation

Codecov / codecov/patch

modules/core/core-blobstore/src/main/java/com/enonic/xp/internal/blobstore/cache/CachedBlobStore.java#L102

Added line #L102 was not covered by tests
}

return record;
try
{
// We don't want return blobs that are not in cache - as we want them to garbage collected ASAP,
// so we dont use get() with loader here
this.cache.put( key, new CacheBlobRecord( record ) );
}
catch ( IOException e )
{
LOG.warn( "Could not cache blob-record with key {}", key, e );
}
// If we could not load the blob into cache, we return the original record
return Objects.requireNonNullElse( this.cache.getIfPresent( key ), record );
}

@Override
Expand Down Expand Up @@ -171,4 +183,58 @@
return (int) Math.min( record.getLength(), Integer.MAX_VALUE );
}
}

private static final class CacheBlobRecord
implements BlobRecord
{
final BlobKey blobKey;

final long lastModified;

final ByteSource content;

CacheBlobRecord( final BlobRecord blobRecord )
throws IOException
{
this.content = ByteSource.wrap( blobRecord.getBytes().read() );
this.blobKey = BlobKey.from( this.content );
if ( !blobRecord.getKey().equals( this.blobKey ) )
{
throw new IOException( String.format( "Cache BlobKey must be the same as the key of the BlobRecord: %s != %s", this.blobKey,
blobRecord.getKey() ) );
}
this.lastModified = blobRecord.lastModified();
}

@Override
public BlobKey getKey()
{
return this.blobKey;

Check warning on line 212 in modules/core/core-blobstore/src/main/java/com/enonic/xp/internal/blobstore/cache/CachedBlobStore.java

View check run for this annotation

Codecov / codecov/patch

modules/core/core-blobstore/src/main/java/com/enonic/xp/internal/blobstore/cache/CachedBlobStore.java#L212

Added line #L212 was not covered by tests
}

@Override
public long getLength()
{
try
{
return content.size();
}
catch ( IOException e )

Check warning on line 222 in modules/core/core-blobstore/src/main/java/com/enonic/xp/internal/blobstore/cache/CachedBlobStore.java

View check run for this annotation

Codecov / codecov/patch

modules/core/core-blobstore/src/main/java/com/enonic/xp/internal/blobstore/cache/CachedBlobStore.java#L222

Added line #L222 was not covered by tests
{
throw new UncheckedIOException( e );

Check warning on line 224 in modules/core/core-blobstore/src/main/java/com/enonic/xp/internal/blobstore/cache/CachedBlobStore.java

View check run for this annotation

Codecov / codecov/patch

modules/core/core-blobstore/src/main/java/com/enonic/xp/internal/blobstore/cache/CachedBlobStore.java#L224

Added line #L224 was not covered by tests
}
}

@Override
public ByteSource getBytes()
{
return this.content;

Check warning on line 231 in modules/core/core-blobstore/src/main/java/com/enonic/xp/internal/blobstore/cache/CachedBlobStore.java

View check run for this annotation

Codecov / codecov/patch

modules/core/core-blobstore/src/main/java/com/enonic/xp/internal/blobstore/cache/CachedBlobStore.java#L231

Added line #L231 was not covered by tests
}

@Override
public long lastModified()
{
return this.lastModified;
}
}
}
Loading
Loading