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

org.teamapps.universaldb.distribute.TransactionReader Maven / Gradle / Ivy

There is a newer version: 0.7.3
Show newest version
/*-
 * ========================LICENSE_START=================================
 * UniversalDB
 * ---
 * Copyright (C) 2014 - 2021 TeamApps.org
 * ---
 * 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.
 * =========================LICENSE_END==================================
 */
package org.teamapps.universaldb.distribute;

import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.serialization.ByteArrayDeserializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.teamapps.universaldb.SchemaStats;
import org.teamapps.universaldb.index.DataBaseMapper;
import org.teamapps.universaldb.transaction.ClusterTransaction;
import org.teamapps.universaldb.transaction.TransactionIdHandler;
import org.teamapps.universaldb.transaction.TransactionPacket;

import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.time.Duration;
import java.util.*;

public class TransactionReader {

	public static final String RESOLVED_SUFFIX = "resolved";
	private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
	private final String clientId;
	private final String masterProducerClientId;
	private final String sharedSecret;
	private final SchemaStats schemaStats;
	private final DataBaseMapper dataBaseMapper;
	private final Consumer consumer;
	private final String topic;
	private final TopicPartition topicPartition;
	private final Map transactionMap;
	private final TransactionIdHandler transactionIdHandler;

	public TransactionReader(ClusterSetConfig clusterConfig,
							 SchemaStats schemaStats,
							 DataBaseMapper dataBaseMapper,
							 Map transactionMap,
							 TransactionIdHandler transactionIdHandler
	) {
		this.sharedSecret = clusterConfig.getSharedSecret();
		this.schemaStats = schemaStats;
		this.dataBaseMapper = dataBaseMapper;
		this.transactionMap = transactionMap;
		this.transactionIdHandler = transactionIdHandler;
		this.clientId = schemaStats.getClientId();
		this.masterProducerClientId = schemaStats.getMasterClientId();
		Properties consumerProps = new Properties();
		consumerProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, clusterConfig.getKafkaConfig());
		consumerProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class.getName());
		consumerProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class.getName());
		consumerProps.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
		consumerProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); //"latest"
		consumerProps.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 1000_000);
		consumerProps.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, 30 * 1024 * 1024);
		consumerProps.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 30 * 1_000);
		consumerProps.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, 9 * 1_000);

		logger.info("Start with transaction offset:" + schemaStats.getTransactionOffset());

		topic = clusterConfig.getTopicPrefix() + "-" + RESOLVED_SUFFIX;
		topicPartition = new TopicPartition(topic, 0);
		consumer = new KafkaConsumer<>(consumerProps);
		consumer.assign(Collections.singletonList(topicPartition));
		consumer.seek(topicPartition, schemaStats.getTransactionOffset());
		new Thread(() -> start()).start();
	}

	private void start() {
		while(true) {
			try {
				consume();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	private void consume() throws IOException {
		ConsumerRecords consumerRecords = consumer.poll(Duration.ofSeconds(1));
		List> records = consumerRecords.records(topicPartition);
		for (ConsumerRecord record : records) {
			TransactionMessageKey messageKey = new TransactionMessageKey(record.key());
			byte[] value = record.value();
			byte[] bytes = PacketDataMingling.mingle(value, sharedSecret, messageKey.getPacketKey());

			TransactionPacket transactionPacket = new TransactionPacket(bytes);
			ClusterTransaction transaction = new ClusterTransaction(transactionPacket, dataBaseMapper);

			if (messageKey.getClientId().equals(clientId)) {
				TransactionExecutionResult executionResult = transactionMap.remove(messageKey.getTransactionKeyOfCallingNode());
				if (executionResult != null) {
					if (value != null) {
						executionResult.handleSuccess(transaction.getRecordIdByCorrelationId());
					} else {
						executionResult.handleError();
					}
				}
			}

			if (!messageKey.getMasterClientId().equals(masterProducerClientId)) {
				if (transaction.getTransactionId() == transactionIdHandler.getLastCommittedTransactionId() + 1) {
					transaction.executeResolvedTransaction(transactionIdHandler);
				} else {
					logger.warn("Transaction with wrong transaction id! Expected id:" + (transactionIdHandler.getLastCommittedTransactionId() + 1) + ", actual id:" + transaction.getTransactionId() + ", key:" + messageKey);
				}
			}

			schemaStats.setTransactionOffset(record.offset() + 1);
			schemaStats.setMasterTransactionOffset(messageKey.getMasterOffset() + 1);

		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy