All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.axoniq.eventstore.client.axon.AxonIQEventStore Maven / Gradle / Ivy

/*
 * Copyright (c) 2017. AxonIQ
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.axoniq.eventstore.client.axon;

import com.google.protobuf.ByteString;
import io.axoniq.eventstore.Event;
import io.axoniq.eventstore.client.AppendEventTransaction;
import io.axoniq.eventstore.client.EventStoreClient;
import io.axoniq.eventstore.client.EventStoreConfiguration;
import io.axoniq.eventstore.client.util.FlowControllingStreamObserver;
import io.axoniq.eventstore.grpc.EventWithToken;
import io.axoniq.eventstore.grpc.GetAggregateEventsRequest;
import io.axoniq.eventstore.grpc.GetEventsRequest;
import org.axonframework.common.Assert;
import org.axonframework.eventhandling.EventMessage;
import org.axonframework.eventsourcing.DomainEventMessage;
import org.axonframework.eventsourcing.GenericDomainEventMessage;
import org.axonframework.eventsourcing.eventstore.*;
import org.axonframework.messaging.unitofwork.CurrentUnitOfWork;
import org.axonframework.serialization.MessageSerializer;
import org.axonframework.serialization.SerializedObject;
import org.axonframework.serialization.Serializer;
import org.axonframework.serialization.upcasting.event.EventUpcaster;
import org.axonframework.serialization.upcasting.event.NoOpEventUpcaster;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

import static org.axonframework.common.ObjectUtils.getOrDefault;

/**
 * Axon EventStore implementation that connects to the AxonIQ Event Store Server to store and retrieve Events.
 *
 * @author Zoltan Altfatter
 * @author Marc Gathier
 * @author Allard Buijze
 */
public class AxonIQEventStore extends AbstractEventStore {

    private static final Logger logger = LoggerFactory.getLogger(AxonIQEventStore.class);

    /**
     * Initialize the Event Store using given {@code configuration} and given {@code serializer}.
     * 

* The Event Store will delay creating the connection until the first activity takes place. * * @param configuration The configuration describing the servers to connect with and how to manage flow control * @param serializer The serializer to serialize Event payloads with */ public AxonIQEventStore(EventStoreConfiguration configuration, Serializer serializer) { this(configuration, serializer, NoOpEventUpcaster.INSTANCE); } /** * Initialize the Event Store using given {@code configuration}, {@code serializer} and {@code upcasterChain} *

* The Event Store will delay creating the connection until the first activity takes place. * * @param configuration The configuration describing the servers to connect with and how to manage flow control * @param serializer The serializer to serialize Event payloads with * @param upcasterChain The upcaster to modify received Event representations with */ public AxonIQEventStore(EventStoreConfiguration configuration, Serializer serializer, EventUpcaster upcasterChain) { super(new AxonIQEventStorageEngine(serializer, upcasterChain, configuration, new EventStoreClient(configuration))); } @Override public TrackingEventStream openStream(TrackingToken trackingToken) { return storageEngine().openStream(trackingToken); } @Override protected AxonIQEventStorageEngine storageEngine() { return (AxonIQEventStorageEngine) super.storageEngine(); } private static class AxonIQEventStorageEngine extends AbstractEventStorageEngine { public static final int ALLOW_SNAPSHOTS_MAGIC_VALUE = -42; private final String APPEND_EVENT_TRANSACTION = this + "/APPEND_EVENT_TRANSACTION"; private final EventUpcaster upcasterChain; private final EventStoreConfiguration configuration; private final EventStoreClient eventStoreClient; private final GrpcMetaDataConverter converter; private AxonIQEventStorageEngine(Serializer serializer, EventUpcaster upcasterChain, EventStoreConfiguration configuration, EventStoreClient eventStoreClient) { super(serializer, upcasterChain, null); this.upcasterChain = upcasterChain; this.configuration = configuration; this.eventStoreClient = eventStoreClient; this.converter = new GrpcMetaDataConverter(serializer); } @Override protected void appendEvents(List> events, Serializer serializer) { AppendEventTransaction sender; if (CurrentUnitOfWork.isStarted()) { sender = CurrentUnitOfWork.get().root().getOrComputeResource(APPEND_EVENT_TRANSACTION, k -> { AppendEventTransaction appendEventTransaction = eventStoreClient.createAppendEventConnection(); CurrentUnitOfWork.get().root().onRollback(u -> appendEventTransaction.rollback(u.getExecutionResult().getExceptionResult())); CurrentUnitOfWork.get().root().onCommit(u -> appendEventTransaction.commit()); return appendEventTransaction; }); } else { sender = eventStoreClient.createAppendEventConnection(); } for (EventMessage eventMessage : events) { sender.append(map(eventMessage)); } if (!CurrentUnitOfWork.isStarted()) { sender.commit(); } } public Event map(EventMessage eventMessage) { Event.Builder builder = Event.newBuilder(); if (eventMessage instanceof GenericDomainEventMessage) { builder.setAggregateIdentifier(((GenericDomainEventMessage) eventMessage).getAggregateIdentifier()) .setAggregateSequenceNumber(((GenericDomainEventMessage) eventMessage).getSequenceNumber()) .setAggregateType(((GenericDomainEventMessage) eventMessage).getType()); } SerializedObject serializedPayload = MessageSerializer.serializePayload(eventMessage, getSerializer(), byte[].class); builder.setMessageIdentifier(eventMessage.getIdentifier()) .setPayload(io.axoniq.eventstore.SerializedObject.newBuilder() .setType(serializedPayload.getType().getName()) .setRevision(getOrDefault(serializedPayload.getType().getRevision(), "")) .setData(ByteString.copyFrom(serializedPayload.getData()))) .setTimestamp(eventMessage.getTimestamp().toEpochMilli()); eventMessage.getMetaData().forEach((k, v) -> builder.putMetaData(k, converter.convertToMetaDataValue(v))); return builder.build(); } @Override protected void storeSnapshot(DomainEventMessage snapshot, Serializer serializer) { try { eventStoreClient.appendSnapshot(map(snapshot)).whenComplete((c, e) -> { if (e != null) { logger.warn("Error occurred while creating a snapshot", e); } else if (c != null) { if (c.getSuccess()) { logger.info("Snapshot created"); } else { logger.warn("Snapshot creation failed for unknown reason. Check server logs for details."); } } }); } catch (Throwable e) { throw AxonErrorMapping.convert(e); } } @Override protected Stream> 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) { request.setAllowSnapshots(true); } try { return eventStoreClient.listAggregateEvents(request.build()).map(GrpcBackedDomainEventData::new); } catch (Exception e) { throw AxonErrorMapping.convert(e); } } public TrackingEventStream openStream(TrackingToken trackingToken) { Assert.isTrue(trackingToken == null || trackingToken instanceof GlobalSequenceTrackingToken, () -> "Invalid tracking token type. Must be GlobalSequenceTrackingToken."); long nextToken = trackingToken == null ? 0 : ((GlobalSequenceTrackingToken) trackingToken).getGlobalIndex() + 1; EventBuffer consumer = new EventBuffer(upcasterChain, getSerializer()); logger.info("open stream: {}", nextToken); FlowControllingStreamObserver observer = new FlowControllingStreamObserver<>( eventStoreClient::listEvents, (eventWithToken, getEventsRequestStreamObserver) -> { logger.debug("Received event with token: {}", eventWithToken.getToken()); consumer.push(eventWithToken); }, throwable -> { logger.error("Failed to receive events", throwable); consumer.fail(new EventStoreException("Error while reading events from the server", throwable)); }); GetEventsRequest request = GetEventsRequest.newBuilder() .setTrackingToken(nextToken) .setNumberOfPermits(configuration.getInitialNrOfPermits()) .build(); GetEventsRequest nextRequest = GetEventsRequest.newBuilder().setNumberOfPermits(configuration.getNrOfNewPermits()).build(); observer.start(request, nextRequest, configuration.getInitialNrOfPermits(), configuration.getNrOfNewPermits(), configuration.getNewPermitsThreshold()); consumer.registerCloseListener((eventConsumer) -> observer.stop()); consumer.registerConsumeListener(observer::markConsumed); return consumer; } @Override public DomainEventStream readEvents(String aggregateIdentifier) { return readEvents(aggregateIdentifier, ALLOW_SNAPSHOTS_MAGIC_VALUE); } @Override protected Stream> readEventData(TrackingToken trackingToken, boolean mayBlock) { throw new UnsupportedOperationException("This method is not optimized for the AxonIQ Event Store and should not be used"); } @Override protected Optional> readSnapshotData(String aggregateIdentifier) { // snapshots are automatically fetched server-side, which is faster return Optional.empty(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy