Search in sources :

Example 41 with StoreException

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);
    }
}
Also used : StoreErrorCodes(com.github.ambry.store.StoreErrorCodes) BlobId(com.github.ambry.commons.BlobId) MessageInfo(com.github.ambry.store.MessageInfo) StoreException(com.github.ambry.store.StoreException)

Example 42 with StoreException

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);
        }
    }
}
Also used : Timer(com.codahale.metrics.Timer) CloudBlobMetadata(com.github.ambry.cloud.CloudBlobMetadata) GeneralSecurityException(java.security.GeneralSecurityException) BlobId(com.github.ambry.commons.BlobId) StoreException(com.github.ambry.store.StoreException)

Example 43 with StoreException

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);
    }
}
Also used : ByteBufferOutputStream(com.github.ambry.utils.ByteBufferOutputStream) GeneralSecurityException(java.security.GeneralSecurityException) IOException(java.io.IOException) ByteBuffer(java.nio.ByteBuffer) StoreException(com.github.ambry.store.StoreException)

Example 44 with StoreException

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));
    }
}
Also used : StoreException(com.github.ambry.store.StoreException)

Example 45 with StoreException

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;
}
Also used : StoreException(com.github.ambry.store.StoreException)

Aggregations

StoreException (com.github.ambry.store.StoreException)59 MessageInfo (com.github.ambry.store.MessageInfo)31 IOException (java.io.IOException)22 ArrayList (java.util.ArrayList)17 BlobId (com.github.ambry.commons.BlobId)16 Store (com.github.ambry.store.Store)16 StoreErrorCodes (com.github.ambry.store.StoreErrorCodes)12 StoreKey (com.github.ambry.store.StoreKey)11 MessageFormatException (com.github.ambry.messageformat.MessageFormatException)10 MockMessageWriteSet (com.github.ambry.store.MockMessageWriteSet)10 DataInputStream (java.io.DataInputStream)10 MessageFormatWriteSet (com.github.ambry.messageformat.MessageFormatWriteSet)9 IdUndeletedStoreException (com.github.ambry.store.IdUndeletedStoreException)8 StoreGetOptions (com.github.ambry.store.StoreGetOptions)8 Test (org.junit.Test)8 PartitionId (com.github.ambry.clustermap.PartitionId)7 MessageFormatInputStream (com.github.ambry.messageformat.MessageFormatInputStream)7 StoreInfo (com.github.ambry.store.StoreInfo)7 DeleteMessageFormatInputStream (com.github.ambry.messageformat.DeleteMessageFormatInputStream)6 ServerNetworkResponseMetrics (com.github.ambry.network.ServerNetworkResponseMetrics)6