io.vlingo.xoom.symbio.store.state.jdbc.JDBCEntriesBatchWriter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xoom-symbio-jdbc Show documentation
Show all versions of xoom-symbio-jdbc Show documentation
Implementation of xoom-symbio for JDBC.
// Copyright © 2012-2021 VLINGO LABS. All rights reserved.
//
// This Source Code Form is subject to the terms of the
// Mozilla Public License, v. 2.0. If a copy of the MPL
// was not distributed with this file, You can obtain
// one at https://mozilla.org/MPL/2.0/.
package io.vlingo.xoom.symbio.store.state.jdbc;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import io.vlingo.xoom.actors.Logger;
import io.vlingo.xoom.common.Failure;
import io.vlingo.xoom.common.Outcome;
import io.vlingo.xoom.common.Success;
import io.vlingo.xoom.symbio.BaseEntry;
import io.vlingo.xoom.symbio.Entry;
import io.vlingo.xoom.symbio.State;
import io.vlingo.xoom.symbio.store.Result;
import io.vlingo.xoom.symbio.store.StorageException;
import io.vlingo.xoom.symbio.store.dispatch.Dispatchable;
import io.vlingo.xoom.symbio.store.dispatch.Dispatcher;
import io.vlingo.xoom.symbio.store.dispatch.DispatcherControl;
public class JDBCEntriesBatchWriter implements JDBCEntriesWriter {
private final JDBCStorageDelegate delegate;
@SuppressWarnings("unused")
private final List, ? extends State>>>> dispatchers;
private final DispatcherControl dispatcherControl;
private final BatchEntries batchEntries;
private Logger logger;
public JDBCEntriesBatchWriter(JDBCStorageDelegate delegate, int maxBatchEntries) {
this(delegate, null, null, maxBatchEntries);
}
public JDBCEntriesBatchWriter(JDBCStorageDelegate delegate,
List, ? extends State>>>> dispatchers,
DispatcherControl dispatcherControl,
int maxBatchEntries) {
this.delegate = delegate;
this.dispatchers = dispatchers;
this.dispatcherControl = dispatcherControl;
this.batchEntries = new BatchEntries(maxBatchEntries);
}
@Override
public void appendEntries(String storeName, List> entries, State.TextState rawState, Consumer> postAppendAction) {
batchEntries.add(new BatchEntry(storeName, entries, rawState, postAppendAction));
if (batchEntries.capacityExceeded()) {
flush();
}
}
@Override
public void flush() {
if (batchEntries.size() > 0) {
appendBatchedEntries();
try {
delegate.beginWrite();
Map> states = batchEntries.states();
for (Map.Entry> storeStates : states.entrySet()) {
final PreparedStatement writeStatesStatement = delegate.writeExpressionFor(storeStates.getKey(), storeStates.getValue());
writeStatesStatement.executeBatch();
writeStatesStatement.clearBatch();
}
List, State>> dispatchables = batchEntries.collectDispatchables();
final PreparedStatement writeDispatchablesStatement = delegate.dispatchableWriteExpressionFor(dispatchables);
writeDispatchablesStatement.executeBatch();
writeDispatchablesStatement.clearBatch();
delegate.complete();
batchEntries.completedWith(Success.of(Result.Success));
batchEntries.clear();
} catch (Exception e) {
logger.error(getClass().getSimpleName() + " appendEntries() failed because: " + e.getMessage(), e);
batchEntries.completedWith(Failure.of(new StorageException(Result.Error, e.getMessage(), e)));
delegate.fail();
}
}
}
@Override
public void stop() {
// flush batched entries if any
flush();
if (dispatcherControl != null) {
dispatcherControl.stop();
}
// delegate is closed in JDBCStateStoreActor
}
@Override
public void setLogger(Logger logger) {
this.logger = logger;
}
@SuppressWarnings("rawtypes")
private void appendBatchedEntries() {
List> allEntries = batchEntries.collectEntries();
if (allEntries.size() > 0) {
try {
PreparedStatement appendStatement = delegate.appendExpressionFor(allEntries);
final int[] countList = appendStatement.executeBatch();
if (Arrays.stream(countList).anyMatch(id -> id == -1L)) {
final String message = "xoom-symbio-jdbc: Failed append entries.";
logger.error(message);
throw new IllegalStateException(message);
}
ResultSet resultSet = appendStatement.getGeneratedKeys();
for (int i = 0; resultSet.next(); i++) {
long id = resultSet.getLong(1);
((BaseEntry) allEntries.get(i)).__internal__setId(Long.toString(id));
}
appendStatement.clearBatch();
} catch (Exception e) {
final String message = "xoom-symbio-jdbc: Failed to append entries because: " + e.getMessage();
logger.error(message, e);
throw new IllegalStateException(message, e);
}
}
}
static class BatchEntries {
private final List entries;
private final int maxCapacity;
BatchEntries(int maxCapacity) {
this.entries = new ArrayList<>(maxCapacity);
this.maxCapacity = maxCapacity;
if (maxCapacity <= 0) {
throw new IllegalArgumentException("Illegal capacity: " + maxCapacity);
}
}
void add(BatchEntry entry) {
entries.add(entry);
}
boolean capacityExceeded() {
return entries.size() >= maxCapacity;
}
List, State>> collectDispatchables() {
return entries.stream()
.map(batch -> batch.getDispatchable())
.collect(Collectors.toList());
}
List> collectEntries() {
return entries.stream()
.map(batch -> batch.entries)
.reduce(new ArrayList>(), (collected, current) -> {
collected.addAll(current);
return collected;
});
}
Map> states() {
return entries.stream()
.collect(Collectors.groupingBy((BatchEntry batch) -> batch.storeName,
Collectors.mapping(batch -> batch.rawState, Collectors.toList())));
}
void completedWith(Outcome outcome) {
entries.stream()
.forEach(batch -> batch.postAppendAction.accept(outcome));
}
void clear() {
entries.clear();
}
int size() {
return entries.size();
}
}
static class BatchEntry {
final String storeName;
final List> entries;
final State.TextState rawState;
final Consumer> postAppendAction;
private Dispatchable, State> dispatchable = null;
boolean failed = false;
BatchEntry(String storeName, List> entries, State.TextState rawState, Consumer> postAppendAction) {
this.storeName = storeName;
this.entries = entries;
this.rawState = rawState;
this.postAppendAction = postAppendAction;
}
public Dispatchable, State> getDispatchable() {
if (dispatchable == null) {
final String dispatchId = storeName + ":" + rawState.id;
dispatchable = new Dispatchable<>(dispatchId, LocalDateTime.now(), rawState.asTextState(), entries);
}
return dispatchable;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy