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

io.descoped.rawdata.provider.postgres.PostgresRawdataProducer Maven / Gradle / Ivy

The newest version!
package io.descoped.rawdata.provider.postgres;

import de.huxhorn.sulky.ulid.ULID;
import io.descoped.rawdata.api.RawdataClosedException;
import io.descoped.rawdata.api.RawdataMessage;
import io.descoped.rawdata.api.RawdataProducer;
import io.descoped.rawdata.provider.postgres.tx.Transaction;
import io.descoped.rawdata.provider.postgres.tx.TransactionFactory;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

class PostgresRawdataProducer implements RawdataProducer {

    private final TransactionFactory transactionFactory;
    private final String topic;
    private final AtomicBoolean closed = new AtomicBoolean(false);
    private final ULID ulid = new ULID();
    private final AtomicReference previousIdRef = new AtomicReference<>(ulid.nextValue());

    PostgresRawdataProducer(TransactionFactory transactionFactory, String topic) {
        this.transactionFactory = transactionFactory;
        this.topic = topic;
    }

    @Override
    public String topic() {
        return topic;
    }

    @Override
    public void publish(RawdataMessage... messages) throws RawdataClosedException {
        try (Transaction tx = transactionFactory.createTransaction(false)) {

            try (PreparedStatement positionUpdate = tx.connection().prepareStatement(String.format("INSERT INTO \"%s_positions\" (ulid, ordering_group, sequence_number, position, ts) VALUES (?, ?, ?, ?, ?)", topic))) {

                try (PreparedStatement contentUpdate = tx.connection().prepareStatement(String.format("INSERT INTO \"%s_content\" (ulid, name, data) VALUES (?, ?, ?)", topic))) {

                    for (RawdataMessage message : messages) {

                        ULID.Value id = getOrGenerateNextUlid(message);
                        UUID uuid = new UUID(id.getMostSignificantBits(), id.getLeastSignificantBits());

                        /*
                         * position
                         */
                        positionUpdate.setObject(1, uuid);
                        positionUpdate.setString(2, message.orderingGroup());
                        positionUpdate.setLong(3, message.sequenceNumber());
                        positionUpdate.setString(4, message.position());
                        positionUpdate.setTimestamp(5, Timestamp.from(new Date(id.timestamp()).toInstant()));
                        positionUpdate.addBatch();

                        /*
                         * content
                         */

                        for (Map.Entry entry : message.data().entrySet()) {
                            contentUpdate.setObject(1, uuid);
                            contentUpdate.setString(2, entry.getKey());
                            contentUpdate.setBytes(3, entry.getValue());
                            contentUpdate.addBatch();
                        }
                    }

                    positionUpdate.executeBatch();

                    contentUpdate.executeBatch();
                }
            }

        } catch (SQLException e) {
            throw new PersistenceException(e);
        }
    }

    private ULID.Value getOrGenerateNextUlid(RawdataMessage message) {
        ULID.Value id = message.ulid();
        while (id == null) {
            ULID.Value previousUlid = previousIdRef.get();
            ULID.Value attemptedId = RawdataProducer.nextMonotonicUlid(this.ulid, previousUlid);
            if (previousIdRef.compareAndSet(previousUlid, attemptedId)) {
                id = attemptedId;
            }
        }
        return id;
    }

    @Override
    public CompletableFuture publishAsync(RawdataMessage... messages) {
        return CompletableFuture.runAsync(() -> publish(messages));
    }

    @Override
    public boolean isClosed() {
        return closed.get();
    }

    @Override
    public void close() {
        closed.set(true);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy