private void commit(AppendEventTransaction appendEventTransaction) { try { appendEventTransaction.commit(); } catch (ExecutionException e) { throw new EventStoreException(e.getMessage(), e.getCause()); } catch (TimeoutException e) { throw new org.axonframework.messaging.ExecutionException("Timeout while executing request", e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new EventStoreException(e.getMessage(), e); } }
/** * Returns a {@link Connection} to the database. * * @return a database Connection. */ protected Connection getConnection() { try { return connectionProvider.getConnection(); } catch (SQLException e) { throw new EventStoreException("Failed to obtain a database connection", e); } }
@Override public void onError(Throwable throwable) { consumer.fail(new EventStoreException( "Error while reading events from the server", throwable )); }
@Override public void onCompleted() { consumer.fail(new EventStoreException("Error while reading events from the server", new RuntimeException("Connection closed by server"))); } });
@Override public void onError(Throwable throwable) { logger.info("Failed to receive events - {}", throwable.getMessage()); consumer.fail(new EventStoreException("Error while reading query results from the server", throwable)); }
@Override public TrackingToken createHeadToken() { try { io.axoniq.axonserver.grpc.event.TrackingToken token = eventStoreClient.getLastToken().get(); return new GlobalSequenceTrackingToken(token.getToken()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new EventStoreException(e.getMessage(), e); } catch (ExecutionException e) { throw new EventStoreException(e.getMessage(), e); } }
@Override public Optional<Long> lastSequenceNumberFor(String aggregateIdentifier) { try { ReadHighestSequenceNrResponse lastSequenceNumber = eventStoreClient .lastSequenceNumberFor(aggregateIdentifier).get(); return lastSequenceNumber.getToSequenceNr() < 0 ? Optional.empty() : Optional.of(lastSequenceNumber.getToSequenceNr()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new EventStoreException(e.getMessage(), e); } catch (ExecutionException e) { throw new EventStoreException(e.getMessage(), e); } }
@Override public TrackingToken createTailToken() { try { io.axoniq.axonserver.grpc.event.TrackingToken token = eventStoreClient.getFirstToken().get(); if (token.getToken() < 0) { return null; } return new GlobalSequenceTrackingToken(token.getToken() - 1); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new EventStoreException(e.getMessage(), e); } catch (ExecutionException e) { throw new EventStoreException(e.getMessage(), e); } }
@Override public TrackingToken createTokenAt(Instant instant) { try { io.axoniq.axonserver.grpc.event.TrackingToken token = eventStoreClient.getTokenAt(instant).get(); if (token.getToken() < 0) { return null; } return new GlobalSequenceTrackingToken(token.getToken() - 1); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new EventStoreException(e.getMessage(), e); } catch (ExecutionException e) { throw new EventStoreException(e.getMessage(), e); } }
@Override protected Stream<? extends DomainEventData<?>> readEventData(String aggregateIdentifier, long firstSequenceNumber) { logger.debug("Reading events for aggregate id {}", aggregateIdentifier); GetAggregateEventsRequest.Builder request = GetAggregateEventsRequest.newBuilder() .setAggregateId(aggregateIdentifier); if (firstSequenceNumber > 0) { request.setInitialSequence(firstSequenceNumber); } else if (firstSequenceNumber == ALLOW_SNAPSHOTS_MAGIC_VALUE && !snapshotFilterSet) { request.setAllowSnapshots(true); } try { return eventStoreClient.listAggregateEvents(request.build()).map(GrpcBackedDomainEventData::new); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new EventStoreException(e.getMessage(), e); } catch (ExecutionException e) { throw new EventStoreException(e.getMessage(), e); } }
@Override public TrackingToken createTailToken() { String sql = "SELECT min(" + schema.globalIndexColumn() + ") - 1 FROM " + schema.domainEventTable(); Long index = transactionManager.fetchInTransaction( () -> executeQuery(getConnection(), connection -> connection.prepareStatement(sql), resultSet -> nextAndExtract(resultSet, 1, Long.class), e -> new EventStoreException("Failed to get tail token", e))); return Optional.ofNullable(index) .map(seq -> GapAwareTrackingToken.newInstance(seq, Collections.emptySet())) .orElse(null); }
@Override public TrackingToken createHeadToken() { String sql = "SELECT max(" + schema.globalIndexColumn() + ") FROM " + schema.domainEventTable(); Long index = transactionManager.fetchInTransaction( () -> executeQuery(getConnection(), connection -> connection.prepareStatement(sql), resultSet -> nextAndExtract(resultSet, 1, Long.class), e -> new EventStoreException("Failed to get head token", e))); return Optional.ofNullable(index) .map(seq -> GapAwareTrackingToken.newInstance(seq, Collections.emptySet())) .orElse(null); }
private List<TrackedEventData<?>> executeEventDataQuery(GapAwareTrackingToken cleanedToken, int batchSize) { return executeQuery( getConnection(), connection -> readEventData(connection, cleanedToken, batchSize), resultSet -> { GapAwareTrackingToken previousToken = cleanedToken; List<TrackedEventData<?>> results = new ArrayList<>(); while (resultSet.next()) { TrackedEventData<?> next = getTrackedEventData(resultSet, previousToken); results.add(next); previousToken = (GapAwareTrackingToken) next.trackingToken(); } return results; }, e -> new EventStoreException(format("Failed to read events from token [%s]", cleanedToken), e) ); }
@Override protected Stream<? extends DomainEventData<?>> readSnapshotData(String aggregateIdentifier) { return transactionManager.fetchInTransaction(() -> { List<DomainEventData<?>> result = executeQuery(getConnection(), connection -> readSnapshotData(connection, aggregateIdentifier), JdbcUtils.listResults(this::getSnapshotData), e -> new EventStoreException( format("Error reading aggregate snapshot [%s]", aggregateIdentifier), e)); return result.stream(); }); }
@Override public Optional<Long> lastSequenceNumberFor(String aggregateIdentifier) { String sql = "SELECT max(" + schema.sequenceNumberColumn() + ") FROM " + schema.domainEventTable() + " WHERE " + schema.aggregateIdentifierColumn() + " = ?"; return Optional.ofNullable(transactionManager.fetchInTransaction( () -> executeQuery(getConnection(), connection -> { PreparedStatement stmt = connection.prepareStatement(sql); stmt.setString(1, aggregateIdentifier); return stmt; }, resultSet -> nextAndExtract(resultSet, 1, Long.class), e -> new EventStoreException( format("Failed to read events for aggregate [%s]", aggregateIdentifier), e ) ))); }
@Override protected List<? extends DomainEventData<?>> fetchDomainEvents(String aggregateIdentifier, long firstSequenceNumber, int batchSize) { return transactionManager.fetchInTransaction( () -> executeQuery( getConnection(), connection -> readEventData(connection, aggregateIdentifier, firstSequenceNumber, batchSize), JdbcUtils.listResults(this::getDomainEventData), e -> new EventStoreException( format("Failed to read events for aggregate [%s]", aggregateIdentifier), e ) )); }
/** * Performs the DDL queries to create the schema necessary for this storage engine implementation. * * @param schemaFactory Factory of the event schema. * @throws EventStoreException when an error occurs executing SQL statements. */ public void createSchema(EventTableFactory schemaFactory) { executeUpdates(getConnection(), e -> { throw new EventStoreException("Failed to create event tables", e); }, connection -> schemaFactory.createDomainEventTable(connection, schema), connection -> schemaFactory.createSnapshotEventTable(connection, schema)); }
/** * Invoke when an Exception is raised while persisting an Event or Snapshot. * * @param exception The exception raised while persisting an Event * @param failedEvent The EventMessage that could not be persisted */ protected void handlePersistenceException(Exception exception, EventMessage<?> failedEvent) { String eventDescription; if (failedEvent instanceof DomainEventMessage<?>) { DomainEventMessage<?> failedDomainEvent = (DomainEventMessage<?>) failedEvent; eventDescription = format("An event for aggregate [%s] at sequence [%d]", failedDomainEvent.getAggregateIdentifier(), failedDomainEvent.getSequenceNumber()); } else { eventDescription = format("An event with identifier [%s]", failedEvent.getIdentifier()); } if (persistenceExceptionResolver != null && persistenceExceptionResolver.isDuplicateKeyViolation(exception)) { throw new ConcurrencyException(eventDescription + " was already inserted", exception); } else { throw new EventStoreException(eventDescription + " could not be persisted", exception); } }
}, e -> new EventStoreException(format("Failed to read events from token [%s]", lastToken), e));
@Override public TrackingToken createTokenAt(Instant dateTime) { String sql = "SELECT min(" + schema.globalIndexColumn() + ") - 1 FROM " + schema.domainEventTable() + " WHERE " + schema.timestampColumn() + " >= ?"; Long index = transactionManager.fetchInTransaction( () -> executeQuery(getConnection(), connection -> { PreparedStatement stmt = connection.prepareStatement(sql); stmt.setString(1, formatInstant(dateTime)); return stmt; }, resultSet -> nextAndExtract(resultSet, 1, Long.class), e -> new EventStoreException(format("Failed to get token at [%s]", dateTime), e))); if (index == null) { return null; } return GapAwareTrackingToken.newInstance(index, Collections.emptySet()); }