use of com.github.ambry.store.StoreException in project ambry by linkedin.
the class CloudBlobStore method delete.
@Override
public void delete(List<MessageInfo> infos) throws StoreException {
checkStarted();
checkDuplicates(infos);
try {
for (MessageInfo msgInfo : infos) {
BlobId blobId = (BlobId) msgInfo.getStoreKey();
// If the cache has been updated by another thread, retry may be avoided
requestAgent.doWithRetries(() -> deleteIfNeeded(blobId, msgInfo.getOperationTimeMs(), msgInfo.getLifeVersion()), "Delete", partitionId.toPathString());
}
} catch (CloudStorageException ex) {
if (ex.getCause() instanceof StoreException) {
throw (StoreException) ex.getCause();
}
StoreErrorCodes errorCode = (ex.getStatusCode() == STATUS_NOT_FOUND) ? StoreErrorCodes.ID_Not_Found : StoreErrorCodes.IOError;
throw new StoreException(ex, errorCode);
}
}
use of com.github.ambry.store.StoreException in project ambry by linkedin.
the class CloudBlobStore method putBlob.
/**
* Upload the blob to the cloud destination.
* @param messageInfo the {@link MessageInfo} containing blob metadata.
* @param messageBuf the bytes to be uploaded.
* @param size the number of bytes to upload.
* @throws CloudStorageException if the upload failed.
*/
private void putBlob(MessageInfo messageInfo, ByteBuffer messageBuf, long size) throws CloudStorageException, IOException, StoreException {
if (shouldUpload(messageInfo)) {
BlobId blobId = (BlobId) messageInfo.getStoreKey();
boolean isRouterEncrypted = isRouterEncrypted(blobId);
EncryptionOrigin encryptionOrigin = isRouterEncrypted ? EncryptionOrigin.ROUTER : EncryptionOrigin.NONE;
boolean encryptThisBlob = requireEncryption && !isRouterEncrypted;
boolean uploaded;
if (encryptThisBlob) {
// Need to encrypt the buffer before upload
long encryptedSize = -1;
Timer.Context encryptionTimer = vcrMetrics.blobEncryptionTime.time();
try {
messageBuf = cryptoAgent.encrypt(messageBuf);
encryptedSize = messageBuf.remaining();
} catch (GeneralSecurityException ex) {
vcrMetrics.blobEncryptionErrorCount.inc();
} finally {
encryptionTimer.stop();
}
vcrMetrics.blobEncryptionCount.inc();
CloudBlobMetadata blobMetadata = new CloudBlobMetadata(blobId, messageInfo.getOperationTimeMs(), messageInfo.getExpirationTimeInMs(), messageInfo.getSize(), EncryptionOrigin.VCR, cryptoAgent.getEncryptionContext(), cryptoAgentFactory.getClass().getName(), encryptedSize, messageInfo.getLifeVersion());
// If buffer was encrypted, we no longer know its size
long bufferLen = (encryptedSize == -1) ? size : encryptedSize;
uploaded = uploadWithRetries(blobId, messageBuf, bufferLen, blobMetadata);
} else {
// PutRequest lifeVersion from frontend is -1. Should set to 0. (0 is the starting life version number for any data).
// Put from replication or recovery should use liferVersion as it's.
short lifeVersion = messageInfo.hasLifeVersion(messageInfo.getLifeVersion()) ? messageInfo.getLifeVersion() : (short) 0;
CloudBlobMetadata blobMetadata = new CloudBlobMetadata(blobId, messageInfo.getOperationTimeMs(), messageInfo.getExpirationTimeInMs(), messageInfo.getSize(), encryptionOrigin, lifeVersion);
uploaded = uploadWithRetries(blobId, messageBuf, size, blobMetadata);
}
addToCache(blobId.getID(), (short) 0, BlobState.CREATED);
if (!uploaded && !isVcr) {
// was uploaded.
throw new StoreException(String.format("Another blob with same key %s exists in store", blobId.getID()), StoreErrorCodes.Already_Exist);
}
} else {
vcrMetrics.blobUploadSkippedCount.inc();
// expiring within {@link CloudConfig#vcrMinTtlDays} for vcr to upload.
if (isVcr && !isExpiringSoon(messageInfo) && !messageInfo.isDeleted()) {
throw new StoreException(String.format("Another blob with same key %s exists in store", messageInfo.getStoreKey().getID()), StoreErrorCodes.Already_Exist);
}
}
}
use of com.github.ambry.store.StoreException in project ambry by linkedin.
the class CloudBlobStore method downloadBlob.
/**
* Download the blob corresponding to the {@code blobId} from the {@code CloudDestination} to the given {@code outputStream}
* If the blob was encrypted by vcr during upload, then this method also decrypts it.
* @param cloudBlobMetadata blob metadata to determine if the blob was encrypted by vcr during upload.
* @param blobId Id of the blob to the downloaded.
* @param outputStream {@code OutputStream} of the donwloaded blob.
* @throws StoreException if there is an error in downloading the blob.
*/
void downloadBlob(CloudBlobMetadata cloudBlobMetadata, BlobId blobId, OutputStream outputStream) throws StoreException {
try {
// TODO: if needed, fetch metadata here and check encryption
if (cloudBlobMetadata.getEncryptionOrigin() == EncryptionOrigin.VCR) {
ByteBuffer encryptedBlob = ByteBuffer.allocate((int) cloudBlobMetadata.getEncryptedSize());
requestAgent.doWithRetries(() -> {
cloudDestination.downloadBlob(blobId, new ByteBufferOutputStream(encryptedBlob));
return null;
}, "Download", cloudBlobMetadata.getPartitionId());
ByteBuffer decryptedBlob = cryptoAgent.decrypt(encryptedBlob);
outputStream.write(decryptedBlob.array());
} else {
requestAgent.doWithRetries(() -> {
cloudDestination.downloadBlob(blobId, outputStream);
return null;
}, "Download", cloudBlobMetadata.getPartitionId());
}
} catch (CloudStorageException | GeneralSecurityException | IOException e) {
throw new StoreException("Error occurred in downloading blob for blobid :" + blobId, StoreErrorCodes.IOError);
}
}
use of com.github.ambry.store.StoreException in project ambry by linkedin.
the class CloudBlobStore method deleteIfNeeded.
/**
* Delete the specified blob if needed depending on the cache state.
* @param blobId the blob to delete
* @param deletionTime the deletion time
* @param lifeVersion life version of the blob.
* @return whether the deletion was performed
* @throws CloudStorageException
*/
private boolean deleteIfNeeded(BlobId blobId, long deletionTime, short lifeVersion) throws CloudStorageException {
String blobKey = blobId.getID();
// in which case the cache may have been updated by another thread.
if (!checkCacheState(blobKey, lifeVersion, BlobState.DELETED)) {
try {
boolean deleted = cloudDestination.deleteBlob(blobId, deletionTime, lifeVersion, this::preDeleteValidation);
addToCache(blobKey, lifeVersion, BlobState.DELETED);
return deleted;
} catch (CloudStorageException ex) {
// Cache entry could be stale, evict it to force refresh on retry.
removeFromCache(blobKey);
throw ex;
}
} else {
// This means that we definitely saw this delete for the same or smaller lifeversion before.
throw new CloudStorageException("Error updating blob metadata", new StoreException("Cannot delete id " + blobId.getID() + " since it is already marked as deleted in cloud.", StoreErrorCodes.ID_Deleted));
}
}
use of com.github.ambry.store.StoreException in project ambry by linkedin.
the class CloudBlobStore method preTtlUpdateValidation.
/**
* Validates existing metadata in cloud destination against requested update for ttl.
* Note that this method also has an unclean side effect of updating the {@code updateFields}.
* @param metadata existing {@link CloudBlobMetadata} in cloud.
* @param key {@link StoreKey} being updated.
* @param updateFields {@link Map} of fields and values being updated.
* @return false only for vcr if ttl is already applied on blob. true in all other cases if validation is successful.
* @throws StoreException if validation fails.
*/
private boolean preTtlUpdateValidation(CloudBlobMetadata metadata, StoreKey key, Map<String, Object> updateFields) throws StoreException {
validateAccountAndContainer(Collections.singletonMap(key.getID(), metadata), Collections.singletonList(key));
long now = System.currentTimeMillis();
if (isVcr) {
// findMissingKeys which in turn is dependent on {@link CloudDestination} implementation and can have some inconsistencies.
return metadata.getExpirationTime() != Utils.Infinite_Time;
}
if (metadata.isDeleted()) {
throw new StoreException("Cannot update TTL of " + key.getID() + " since it is already deleted in the index.", StoreErrorCodes.ID_Deleted);
} else if (metadata.getExpirationTime() != Utils.Infinite_Time && metadata.getExpirationTime() < now + ttlUpdateBufferTimeMs) {
throw new StoreException("TTL of " + key.getID() + " cannot be updated because it is too close to expiry. Minimum Op time (ms): " + now + ". ExpiresAtMs: " + metadata.getExpirationTime(), StoreErrorCodes.Update_Not_Allowed);
}
return true;
}
Aggregations