Search in sources :

Example 1 with MetadataConflictException

use of io.cdap.cdap.common.metadata.MetadataConflictException in project cdap by caskdata.

the class ElasticsearchMetadataStorage method batch.

@Override
public List<MetadataChange> batch(List<? extends MetadataMutation> mutations, MutationOptions options) throws IOException {
    if (mutations.isEmpty()) {
        return Collections.emptyList();
    }
    if (mutations.size() == 1) {
        return Collections.singletonList(apply(mutations.get(0), options));
    }
    // first detect whether there are duplicate entity ids. If so, execute in sequence
    Set<MetadataEntity> entities = new HashSet<>();
    LinkedHashMap<MetadataEntity, MetadataMutation> mutationMap = new LinkedHashMap<>(mutations.size());
    boolean duplicate = false;
    for (MetadataMutation mutation : mutations) {
        if (!entities.add(mutation.getEntity())) {
            duplicate = true;
            break;
        }
        mutationMap.put(mutation.getEntity(), mutation);
    }
    if (duplicate) {
        // if there are multiple mutations for the same entity, execute all in sequence
        List<MetadataChange> changes = new ArrayList<>(mutations.size());
        for (MetadataMutation mutation : mutations) {
            changes.add(apply(mutation, options));
        }
        return changes;
    }
    // collect all changes in an order-preserving map. The first time doBatch() is called, it will
    // enter all entities in the map. Every time it is retried, the change may get updated, but that
    // will not change the order of the map.
    LinkedHashMap<MetadataEntity, MetadataChange> changes = new LinkedHashMap<>(mutations.size());
    try {
        // repeatedly try to read current metadata, apply the mutations and reindex, until there is no conflict
        return Retries.callWithRetries(() -> doBatch(mutationMap, changes, options), RetryStrategies.limit(50, RetryStrategies.fixDelay(100, TimeUnit.MILLISECONDS)), e -> e instanceof MetadataConflictException);
    } catch (MetadataConflictException e) {
        throw new MetadataConflictException("After retries: " + e.getRawMessage(), e.getConflictingEntities());
    }
}
Also used : MetadataConflictException(io.cdap.cdap.common.metadata.MetadataConflictException) MetadataEntity(io.cdap.cdap.api.metadata.MetadataEntity) MetadataMutation(io.cdap.cdap.spi.metadata.MetadataMutation) MetadataChange(io.cdap.cdap.spi.metadata.MetadataChange) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) LinkedHashMap(java.util.LinkedHashMap)

Example 2 with MetadataConflictException

use of io.cdap.cdap.common.metadata.MetadataConflictException in project cdap by caskdata.

the class ElasticsearchMetadataStorage method executeMutation.

/**
 * Executes an ElasticSearch request to modify a document (index or delete), and handles possible failure.
 *
 * @throws MetadataConflictException if a conflict occurs
 * @throws IOException for any other problem encountered
 */
private void executeMutation(MetadataEntity entity, WriteRequest<?> writeRequest, MutationOptions options) throws IOException {
    String requestType = null;
    DocWriteResponse response;
    setRefreshPolicy(writeRequest, options);
    RestHighLevelClient client = getClient();
    try {
        if (writeRequest instanceof DeleteRequest) {
            requestType = "Delete";
            response = client.delete((DeleteRequest) writeRequest, RequestOptions.DEFAULT);
        } else if (writeRequest instanceof IndexRequest) {
            requestType = "Index";
            response = client.index((IndexRequest) writeRequest, RequestOptions.DEFAULT);
        } else {
            throw new IllegalStateException("Unexpected WriteRequest of type " + writeRequest.getClass().getName());
        }
    } catch (ElasticsearchStatusException e) {
        if (isConflict(e.status())) {
            LOG.debug("Encountered conflict in {} request for entity {}", requestType, entity);
            throw new MetadataConflictException(requestType + " conflict for ${conflicting}", entity);
        }
        throw e;
    }
    if (isNotFound(response.status())) {
        // ignore entities that do not exist - only happens for deletes
        return;
    }
    if (isConflict(response.status())) {
        LOG.debug("Encountered conflict in {} request for entity {}", requestType, entity);
        throw new MetadataConflictException(requestType + " conflict for ${conflicting}", entity);
    }
    if (isFailure(response)) {
        throw new IOException(String.format(requestType + " request unsuccessful for entity %s: %s", entity, response));
    }
}
Also used : MetadataConflictException(io.cdap.cdap.common.metadata.MetadataConflictException) DocWriteResponse(org.elasticsearch.action.DocWriteResponse) RestHighLevelClient(org.elasticsearch.client.RestHighLevelClient) IOException(java.io.IOException) IndexRequest(org.elasticsearch.action.index.IndexRequest) CreateIndexRequest(org.elasticsearch.action.admin.indices.create.CreateIndexRequest) DeleteIndexRequest(org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest) GetIndexRequest(org.elasticsearch.action.admin.indices.get.GetIndexRequest) DeleteRequest(org.elasticsearch.action.delete.DeleteRequest) ElasticsearchStatusException(org.elasticsearch.ElasticsearchStatusException)

Example 3 with MetadataConflictException

use of io.cdap.cdap.common.metadata.MetadataConflictException in project cdap by caskdata.

the class ElasticsearchMetadataStorage method executeBulk.

/**
 * Executes a bulk request and handles the responses for possible failures, and removes all successful
 * mutations from the mutations map.
 *
 * @param mutations the mutations represented by this bulk. Every mutation that is successfully executed
 *                  is removed from this map, even if an exception is thrown.
 *
 * @throws MetadataConflictException if a conflict occurs for any of the operations in the bulk
 * @throws IOException for any other problem encountered
 */
private void executeBulk(BulkRequest bulkRequest, LinkedHashMap<MetadataEntity, MetadataMutation> mutations) throws IOException {
    RestHighLevelClient client = getClient();
    BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
    if (response.hasFailures()) {
        IOException ioe = null;
        List<MetadataEntity> conflictEntities = new ArrayList<>();
        for (BulkItemResponse itemResponse : response) {
            MetadataEntity entityId;
            try {
                entityId = toMetadataEntity(itemResponse.getId());
            } catch (Exception e) {
                LOG.warn("Cannot parse entity id from document id {} in bulk response", itemResponse.getId());
                continue;
            }
            if (!itemResponse.isFailed()) {
                // remove this mutation - it was successful
                mutations.remove(entityId);
                continue;
            }
            if (isNotFound(itemResponse.status())) {
                // only happens for deletes - we consider this successful
                mutations.remove(entityId);
                continue;
            }
            // this mutation failed
            BulkItemResponse.Failure failure = itemResponse.getFailure();
            if (isConflict(failure.getStatus())) {
                conflictEntities.add(entityId);
                continue;
            }
            // not a conflict -> true failure
            if (ioe == null) {
                ioe = new IOException("Bulk request unsuccessful");
            }
            ioe.addSuppressed(new IOException(String.format("%s request unsuccessful for entity %s: %s", itemResponse.getOpType(), entityId, failure.getMessage())));
        }
        if (ioe != null) {
            throw ioe;
        }
        if (!conflictEntities.isEmpty()) {
            LOG.debug("Encountered conflicts in batch mutation for entities {}", conflictEntities);
            throw new MetadataConflictException("Bulk request conflicts for entities ${conflicting}", conflictEntities);
        }
    }
}
Also used : MetadataConflictException(io.cdap.cdap.common.metadata.MetadataConflictException) MetadataEntity(io.cdap.cdap.api.metadata.MetadataEntity) ArrayList(java.util.ArrayList) BulkItemResponse(org.elasticsearch.action.bulk.BulkItemResponse) BulkResponse(org.elasticsearch.action.bulk.BulkResponse) RestHighLevelClient(org.elasticsearch.client.RestHighLevelClient) IOException(java.io.IOException) KeyStoreException(java.security.KeyStoreException) KeyManagementException(java.security.KeyManagementException) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) IOException(java.io.IOException) MetadataConflictException(io.cdap.cdap.common.metadata.MetadataConflictException) ElasticsearchStatusException(org.elasticsearch.ElasticsearchStatusException)

Example 4 with MetadataConflictException

use of io.cdap.cdap.common.metadata.MetadataConflictException in project cdap by caskdata.

the class ElasticsearchMetadataStorage method apply.

@Override
public MetadataChange apply(MetadataMutation mutation, MutationOptions options) throws IOException {
    MetadataEntity entity = mutation.getEntity();
    try {
        // repeatedly try to read current metadata, apply the mutation and reindex, until there is no conflict
        return Retries.callWithRetries(() -> {
            VersionedMetadata before = readFromIndex(entity);
            RequestAndChange intermediary = applyMutation(before, mutation);
            executeMutation(mutation.getEntity(), intermediary.getRequest(), options);
            return intermediary.getChange();
        }, retryStrategyOnConflict, e -> e instanceof MetadataConflictException);
    } catch (MetadataConflictException e) {
        throw new MetadataConflictException("After retries: " + e.getRawMessage(), e.getConflictingEntities());
    }
}
Also used : MetadataConflictException(io.cdap.cdap.common.metadata.MetadataConflictException) MetadataEntity(io.cdap.cdap.api.metadata.MetadataEntity)

Aggregations

MetadataConflictException (io.cdap.cdap.common.metadata.MetadataConflictException)4 MetadataEntity (io.cdap.cdap.api.metadata.MetadataEntity)3 IOException (java.io.IOException)2 ArrayList (java.util.ArrayList)2 ElasticsearchStatusException (org.elasticsearch.ElasticsearchStatusException)2 RestHighLevelClient (org.elasticsearch.client.RestHighLevelClient)2 MetadataChange (io.cdap.cdap.spi.metadata.MetadataChange)1 MetadataMutation (io.cdap.cdap.spi.metadata.MetadataMutation)1 KeyManagementException (java.security.KeyManagementException)1 KeyStoreException (java.security.KeyStoreException)1 NoSuchAlgorithmException (java.security.NoSuchAlgorithmException)1 HashSet (java.util.HashSet)1 LinkedHashMap (java.util.LinkedHashMap)1 DocWriteResponse (org.elasticsearch.action.DocWriteResponse)1 CreateIndexRequest (org.elasticsearch.action.admin.indices.create.CreateIndexRequest)1 DeleteIndexRequest (org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest)1 GetIndexRequest (org.elasticsearch.action.admin.indices.get.GetIndexRequest)1 BulkItemResponse (org.elasticsearch.action.bulk.BulkItemResponse)1 BulkResponse (org.elasticsearch.action.bulk.BulkResponse)1 DeleteRequest (org.elasticsearch.action.delete.DeleteRequest)1