[#870] Stop replaying a rolled back read in the PersistIt backend (#871)
| | |
| | | @Override |
| | | public <T> T read(ReadOperation<T> operation) throws Exception |
| | | { |
| | | /* |
| | | * A rolled back read is not replayed. Unlike WriteOperation, a ReadOperation is not required to be |
| | | * idempotent, and four of them are not: ExportJob has written entries to its LDIF stream, whose writer is |
| | | * opened once so that a replay appends rather than truncates; VerifyJob has accumulated its counters in |
| | | * instance fields that no attempt resets; and the two reads of BackendStat have printed records and |
| | | * appended to a map owned by their caller. Replaying corrupts their result rather than repairing it, so |
| | | * the failure goes to the caller, as it does in the JE, Cassandra and JDBC backends. |
| | | */ |
| | | final Transaction txn = db.getTransaction(); |
| | | for (;;) |
| | | txn.begin(); |
| | | try |
| | | { |
| | | txn.begin(); |
| | | try |
| | | { |
| | | final T result = operation.run(this); |
| | | txn.commit(commitPolicy); |
| | | return result; |
| | | } |
| | | catch (final RollbackException e) |
| | | { |
| | | // retry |
| | | } |
| | | catch (final Exception e) |
| | | { |
| | | txn.rollback(); |
| | | throw e; |
| | | } |
| | | finally |
| | | { |
| | | txn.end(); |
| | | } |
| | | final T result = operation.run(this); |
| | | txn.commit(commitPolicy); |
| | | return result; |
| | | } |
| | | catch (final Exception e) |
| | | { |
| | | txn.rollback(); |
| | | throw e; |
| | | } |
| | | finally |
| | | { |
| | | txn.end(); |
| | | } |
| | | } |
| | | |
| | |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2014-2015 ForgeRock AS. |
| | | * Portions Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.server.backends.pluggable.spi; |
| | | |
| | |
| | | { |
| | | /** |
| | | * Executes a read operation, and returns the read value. |
| | | * <p> |
| | | * Implementations need not be idempotent: unlike {@link WriteOperation}, a read operation is never replayed by |
| | | * {@link Storage#read(ReadOperation)}, so it is free to print, to write to a stream, or to accumulate state |
| | | * outside of the transaction. |
| | | * |
| | | * @param txn |
| | | * the read transaction where to execute the read operation |
| | |
| | | * information: "Portions Copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2014-2015 ForgeRock AS. |
| | | * Portions Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.server.backends.pluggable.spi; |
| | | |
| | |
| | | void open(AccessMode accessMode) throws Exception; |
| | | |
| | | /** |
| | | * Executes a read operation. In case of a read operation rollback, implementations must ensure |
| | | * the read operation is retried until it succeeds. |
| | | * Executes a read operation. In case of a read operation rollback, implementations must propagate the failure |
| | | * to the caller rather than replay the operation: unlike {@link WriteOperation}, a {@link ReadOperation} is not |
| | | * required to be idempotent, and several of them are not - they write to a stream, print, or accumulate state |
| | | * that a second attempt would double. |
| | | * |
| | | * @param <T> |
| | | * type of the value returned |