Skip to content
This repository was archived by the owner on Jun 7, 2023. It is now read-only.

Commit 68af1a4

Browse files
author
Rustam Aliyev
committed
Refactored blob storage. DB threshold now compared to final blob size
instead of original.
1 parent ca9b940 commit 68af1a4

File tree

13 files changed

+201
-226
lines changed

13 files changed

+201
-226
lines changed

config/elasticinbox.yaml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ store_plain_message: false
4242

4343
# Maximum blob size in bytes which can be stored in the database.
4444
# Blobs larger than this value will be stored with the deafult blob profile (blobstore_write_profile).
45-
# Set to 0 to disable using database for blobs. Currently value can't be more than 128K.
46-
database_blob_max_size: 65536
45+
# If compression enabled, this threshold will be applied to a compressed blob size.
46+
# Set to 0 to disable using database as a blob storage. Maximum allowed value 128K.
47+
database_blob_max_size: 32768
4748

4849
### Cassandra settings
4950
# Specify Cassandra hosts (multiple for LB), cluster name, keyspace
@@ -88,14 +89,15 @@ blobstore_profiles:
8889
endpoint: /u02/domains
8990
container: elasticinbox
9091

91-
# deafult profile to use for writing messages to blob storage
92-
# only one profile can be used for writing at the same time
92+
# Deafult profile to use for writing messages to blob storage
93+
# only one profile can be used for writing at the same time.
9394
blobstore_write_profile: fs-local
9495

95-
# compress objects written to the blob store (including database blobs)
96+
# Compress objects written to the blob store (including database blobs)
9697
blobstore_enable_compression: true
9798

98-
# encrypt objects written to the blob store (excluding database blobs)
99+
# Encrypt objects written to the blob store. Blobs stored in database are
100+
# never encrypted.
99101
blobstore_enable_encryption: false
100102
#blobstore_default_encryption_key: mykey1
101103

itests/src/test/resources/elasticinbox.yaml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ store_plain_message: false
4242

4343
# Maximum blob size in bytes which can be stored in the database.
4444
# Blobs larger than this value will be stored with the deafult blob profile (blobstore_write_profile).
45-
# Set to 0 to disable using database for blobs. Currently value can't be more than 128K.
46-
database_blob_max_size: 65536
45+
# If compression enabled, this threshold will be applied to a compressed blob size.
46+
# Set to 0 to disable using database as a blob storage. Maximum allowed value 128K.
47+
database_blob_max_size: 32768
4748

4849
### Cassandra settings
4950
# Specify Cassandra hosts (multiple for LB), cluster name, keyspace
@@ -74,14 +75,15 @@ blobstore_profiles:
7475
identity: itest
7576
credential: itest
7677

77-
# deafult profile to use for writing messages to blob storage
78-
# only one profile can be used for writing at the same time
78+
# Deafult profile to use for writing messages to blob storage
79+
# only one profile can be used for writing at the same time.
7980
blobstore_write_profile: itest
8081

81-
# compress objets writted to the blob store
82+
# Compress objects written to the blob store (including database blobs)
8283
blobstore_enable_compression: true
8384

84-
# encrypt objects written to the blob store
85+
# Encrypt objects written to the blob store. Blobs stored in database are
86+
# never encrypted.
8587
blobstore_enable_encryption: true
8688
blobstore_default_encryption_key: testkey2
8789

modules/core/src/main/java/com/elasticinbox/core/blob/BlobDataSource.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ public class BlobDataSource
4848
private final BlobURI blobUri;
4949
private final CompressionHandler compressionHandler;
5050

51+
public BlobDataSource(final URI uri, final InputStream in)
52+
{
53+
this(uri, in, null);
54+
}
55+
5156
public BlobDataSource(final URI uri, final InputStream in, final CompressionHandler ch)
5257
{
5358
this.blobUri = new BlobURI().fromURI(uri);
@@ -63,11 +68,7 @@ public BlobDataSource(final URI uri, final InputStream in, final CompressionHand
6368
*/
6469
public boolean isCompressed()
6570
{
66-
return ((blobUri.getCompression() != null && blobUri.getCompression()
67-
.equals(DeflateCompressionHandler.COMPRESSION_TYPE_DEFLATE)) ||
68-
// TODO: deprecated suffix based compression detection
69-
// kept for backward compatibility with 0.3
70-
blobUri.getName().endsWith(BlobStoreConstants.COMPRESS_SUFFIX));
71+
return (this.compressionHandler != null);
7172
}
7273

7374
/**
@@ -94,7 +95,7 @@ public InputStream getInputStream() throws IOException {
9495
*/
9596
public InputStream getUncompressedInputStream() throws IOException
9697
{
97-
if (this.isCompressed() && this.compressionHandler != null) {
98+
if (this.isCompressed()) {
9899
return this.compressionHandler.uncompress(in);
99100
} else {
100101
return in;

modules/core/src/main/java/com/elasticinbox/core/blob/store/AbstractBlobStorage.java

Lines changed: 0 additions & 50 deletions
This file was deleted.

modules/core/src/main/java/com/elasticinbox/core/blob/store/BlobStorage.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.util.UUID;
3636

3737
import com.elasticinbox.core.blob.BlobDataSource;
38+
import com.elasticinbox.core.blob.BlobURI;
3839
import com.elasticinbox.core.model.Mailbox;
3940

4041
public interface BlobStorage
@@ -56,9 +57,9 @@ public interface BlobStorage
5657
* @throws IOException
5758
* @throws GeneralSecurityException
5859
*/
59-
public URI write(final UUID messageId, final Mailbox mailbox, final String profileName, final InputStream in, final Long size)
60+
public BlobURI write(final UUID messageId, final Mailbox mailbox, final String profileName, final InputStream in, final Long size)
6061
throws IOException, GeneralSecurityException;
61-
62+
6263
/**
6364
* Read blob contents and decrypt
6465
*

modules/core/src/main/java/com/elasticinbox/core/blob/store/BlobStorageMediator.java

Lines changed: 85 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,66 +28,134 @@
2828

2929
package com.elasticinbox.core.blob.store;
3030

31+
import static com.elasticinbox.core.blob.store.BlobStoreConstants.*;
32+
3133
import java.io.IOException;
3234
import java.io.InputStream;
3335
import java.net.URI;
3436
import java.security.GeneralSecurityException;
3537
import java.util.UUID;
3638

39+
import org.slf4j.Logger;
40+
import org.slf4j.LoggerFactory;
41+
3742
import com.elasticinbox.common.utils.Assert;
3843
import com.elasticinbox.config.Configurator;
3944
import com.elasticinbox.config.DatabaseConstants;
4045
import com.elasticinbox.core.blob.BlobDataSource;
4146
import com.elasticinbox.core.blob.BlobURI;
4247
import com.elasticinbox.core.blob.compression.CompressionHandler;
48+
import com.elasticinbox.core.blob.compression.DeflateCompressionHandler;
4349
import com.elasticinbox.core.blob.encryption.EncryptionHandler;
4450
import com.elasticinbox.core.model.Mailbox;
51+
import com.google.common.io.ByteStreams;
52+
import com.google.common.io.FileBackedOutputStream;
4553

4654
/**
47-
* Blob storage mediator is an abstraction layer which contains logic which
55+
* Blob storage mediator is an abstraction layer containing logic which
4856
* determines where to store or how to access given blob.
4957
*
5058
* @author Rustam Aliyev
5159
*/
5260
public final class BlobStorageMediator implements BlobStorage
5361
{
62+
private static final Logger logger =
63+
LoggerFactory.getLogger(BlobStorageMediator.class);
64+
65+
protected final CompressionHandler compressionHandler;
66+
5467
private BlobStorage cloudBlobStorage;
5568
private BlobStorage dbBlobStorage;
5669

57-
public BlobStorageMediator(CompressionHandler ch, EncryptionHandler eh) {
58-
cloudBlobStorage = new CloudBlobStorage(ch, eh);
59-
dbBlobStorage = new CassandraBlobStorage(ch, eh);
70+
/**
71+
* Initialise mediator with compression and encryption handlers for writes.
72+
* To disable compression/encryption set value to null.
73+
*
74+
* @param ch
75+
* Injected compression handler
76+
* @param eh
77+
* Injected encryption handler
78+
*/
79+
public BlobStorageMediator(final CompressionHandler ch, final EncryptionHandler eh)
80+
{
81+
this.compressionHandler = ch;
82+
cloudBlobStorage = new CloudBlobStorage(eh);
83+
dbBlobStorage = new CassandraBlobStorage();
6084
}
61-
62-
public URI write(UUID messageId, Mailbox mailbox, String profileName,
63-
InputStream in, Long size) throws IOException,
85+
86+
public BlobURI write(final UUID messageId, final Mailbox mailbox, final String profileName,
87+
final InputStream in, final Long size) throws IOException,
6488
GeneralSecurityException
6589
{
6690
Assert.notNull(in, "No data to store");
6791

68-
if (size <= Configurator.getDatabaseBlobMaxSize()) {
69-
return dbBlobStorage.write(messageId, mailbox, null, in, size);
92+
BlobURI blobUri;
93+
InputStream in1;
94+
Long updatedSize = size;
95+
boolean compressed = false;
96+
97+
// compress stream and calculate compressed size
98+
if ((compressionHandler != null) && (size > MIN_COMPRESS_SIZE))
99+
{
100+
InputStream compressedInputStream = compressionHandler.compress(in);
101+
FileBackedOutputStream fbout = new FileBackedOutputStream(MAX_MEMORY_FILE_SIZE, true);
102+
updatedSize = ByteStreams.copy(compressedInputStream, fbout);
103+
in1 = fbout.getSupplier().getInput();
104+
compressed = true;
105+
} else {
106+
in1 = in;
107+
}
108+
109+
if (updatedSize <= Configurator.getDatabaseBlobMaxSize())
110+
{
111+
logger.debug(
112+
"Storing Blob in the database because size ({}KB) was less than database threshold {}KB",
113+
updatedSize, Configurator.getDatabaseBlobMaxSize());
114+
blobUri = dbBlobStorage.write(messageId, mailbox, null, in1, updatedSize);
70115
} else {
71-
return cloudBlobStorage.write(messageId, mailbox, Configurator.getBlobStoreWriteProfileName(), in, size);
116+
logger.debug(
117+
"Storing Blob in the cloud because size ({}KB) was greater than database threshold {}KB",
118+
updatedSize, Configurator.getDatabaseBlobMaxSize());
119+
blobUri = cloudBlobStorage.write(messageId, mailbox, Configurator.getBlobStoreWriteProfileName(), in1, updatedSize);
72120
}
121+
122+
// add compression information to the blob URI
123+
if (compressed) {
124+
blobUri.setCompression(compressionHandler.getType());
125+
}
126+
127+
return blobUri;
73128
}
74129

75-
public BlobDataSource read(URI uri) throws IOException
130+
public BlobDataSource read(final URI uri) throws IOException
76131
{
77132
// check if blob was stored for the message
78133
Assert.notNull(uri, "URI cannot be null");
79134

80-
boolean isDbProfile = new BlobURI().fromURI(uri).getProfile()
81-
.equals(DatabaseConstants.DATABASE_PROFILE);
135+
BlobDataSource blobDS;
136+
BlobURI blobUri = new BlobURI().fromURI(uri);
82137

83-
if (isDbProfile) {
84-
return dbBlobStorage.read(uri);
138+
if (blobUri.getProfile().equals(DatabaseConstants.DATABASE_PROFILE)) {
139+
blobDS = dbBlobStorage.read(uri);
140+
} else {
141+
blobDS = cloudBlobStorage.read(uri);
142+
}
143+
144+
// if compressed, add compression handler to data source
145+
if ((blobUri.getCompression() != null && blobUri.getCompression()
146+
.equals(DeflateCompressionHandler.COMPRESSION_TYPE_DEFLATE)) ||
147+
// TODO: deprecated suffix based compression detection
148+
// kept for backward compatibility with 0.3
149+
blobUri.getName().endsWith(BlobStoreConstants.COMPRESS_SUFFIX))
150+
{
151+
CompressionHandler ch = new DeflateCompressionHandler();
152+
return new BlobDataSource(uri, blobDS.getInputStream(), ch);
85153
} else {
86-
return cloudBlobStorage.read(uri);
154+
return blobDS;
87155
}
88156
}
89157

90-
public void delete(URI uri) throws IOException
158+
public void delete(final URI uri) throws IOException
91159
{
92160
// check if blob was stored for the message, silently skip otherwise
93161
if (uri == null) {

modules/core/src/main/java/com/elasticinbox/core/blob/store/BlobStoreConstants.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,14 @@ public final class BlobStoreConstants
5656
public static final String COMPRESS_SUFFIX = ".dfl";
5757

5858
/** Files smaller that this parameter should not be compressed. In bytes. */
59-
public static final Integer MIN_COMPRESS_SIZE = 512;
59+
public static final Integer MIN_COMPRESS_SIZE = 256;
6060

6161
/** Default block ID. Currently only single block DB operations supported. */
6262
public static final int DATABASE_DEFAULT_BLOCK_ID = 0;
6363

64+
/** Threshold for switching from memory to file based buffering **/
65+
public static final int MAX_MEMORY_FILE_SIZE = 204800; // 200KB
66+
6467
/**
6568
* Providers that are independently configurable. Currently invisible form jClouds.
6669
*

0 commit comments

Comments
 (0)