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

com.elefana.util.TableIndexCreator Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright 2018 Viridian Software Limited
 *
 * 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 com.elefana.util;

import com.elefana.api.indices.IndexGenerationMode;
import com.elefana.api.indices.IndexGenerationSettings;
import com.elefana.node.NodeInfoService;
import com.elefana.node.NodeSettingsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.concurrent.*;

@Service
@DependsOn("nodeInfoService")
public class TableIndexCreator implements Runnable {
	private static final Logger LOGGER = LoggerFactory.getLogger(TableIndexCreator.class);

	private final DelayQueue tableIndexCreationQueue = new DelayQueue();
	private final DelayQueue fieldIndexCreationQueue = new DelayQueue();
	private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

	@Autowired
	private Environment environment;
	@Autowired
	private JdbcTemplate jdbcTemplate;
	@Autowired
	private NodeInfoService nodeInfoService;
	@Autowired
	private NodeSettingsService nodeSettingsService;

	@PostConstruct
	public void postConstruct() throws SQLException {
		if(!nodeInfoService.isMasterNode()) {
			//Only master node can create indices
			scheduledExecutorService.shutdown();
			return;
		}
		final long interval = Math.min(nodeSettingsService.getFieldStatsInterval(), nodeSettingsService.getMappingInterval());
		scheduledExecutorService.scheduleAtFixedRate(this, 1L, Math.max(1000, interval), TimeUnit.MILLISECONDS);
	}

	@PreDestroy
	public void preDestroy() {
		if(!nodeInfoService.isMasterNode()) {
			return;
		}
		scheduledExecutorService.shutdown();
	}

	@Override
	public void run() {
		runTableIndexCreation();
		runFieldIndexCreation();
	}

	private void runTableIndexCreation() {
		final DelayedTableIndexCreation nextIndexCreation = tableIndexCreationQueue.poll();
		if(nextIndexCreation == null) {
			return;
		}

		Connection connection = null;
		try {
			connection = jdbcTemplate.getDataSource().getConnection();

			internalCreatePsqlIndices(connection, nextIndexCreation.tableName, nextIndexCreation.indexGenerationSettings);

			connection.close();
		} catch (Exception e) {
			if (connection != null) {
				try {
					connection.close();
				} catch (SQLException e1) {
					e1.printStackTrace();
				}
			}
			e.printStackTrace();
		}
	}

	private void runFieldIndexCreation() {
		final DelayedFieldIndexCreation nextIndexCreation = fieldIndexCreationQueue.poll();
		if(nextIndexCreation == null) {
			return;
		}

		Connection connection = null;
		try {
			connection = jdbcTemplate.getDataSource().getConnection();

			internalCreatePsqlIndex(connection, nextIndexCreation.tableName, nextIndexCreation.fieldName, nextIndexCreation.indexGenerationSettings);

			connection.close();
		} catch (Exception e) {
			if (connection != null) {
				try {
					connection.close();
				} catch (SQLException e1) {
					e1.printStackTrace();
				}
			}
			e.printStackTrace();
		}
	}

	public void createPsqlIndices(Connection connection, String tableName, IndexGenerationSettings indexGenerationSettings) throws SQLException {
		if(indexGenerationSettings.getIndexDelaySeconds() <= 0) {
			internalCreatePsqlIndices(connection, tableName, indexGenerationSettings);
		} else {
			final DelayedTableIndexCreation delayedTableIndexCreation = new DelayedTableIndexCreation();
			delayedTableIndexCreation.indexGenerationSettings = indexGenerationSettings;
			delayedTableIndexCreation.tableName = tableName;
			delayedTableIndexCreation.delayMillis = TimeUnit.SECONDS.toMillis(indexGenerationSettings.getIndexDelaySeconds());
			tableIndexCreationQueue.offer(delayedTableIndexCreation);
		}
	}

	public void createPsqlIndex(Connection connection, String tableName, String fieldName, IndexGenerationSettings indexGenerationSettings) throws SQLException {
		if(indexGenerationSettings.getIndexDelaySeconds() <= 0) {
			internalCreatePsqlIndex(connection, tableName, fieldName, indexGenerationSettings);
		} else {
			final DelayedFieldIndexCreation delayedFieldIndexCreation = new DelayedFieldIndexCreation();
			delayedFieldIndexCreation.indexGenerationSettings = indexGenerationSettings;
			delayedFieldIndexCreation.tableName = tableName;
			delayedFieldIndexCreation.fieldName = fieldName;
			delayedFieldIndexCreation.delayMillis = TimeUnit.SECONDS.toMillis(indexGenerationSettings.getIndexDelaySeconds());
			fieldIndexCreationQueue.offer(delayedFieldIndexCreation);
		}
	}

	private void internalCreatePsqlIndex(Connection connection, String tableName, String fieldName, IndexGenerationSettings indexGenerationSettings) throws SQLException {
		switch(indexGenerationSettings.getMode()) {
		case ALL:
			return;
		case PRESET:
		case DYNAMIC:
			final String btreeIndexName = IndexUtils.BTREE_INDEX_PREFIX + tableName + "_" + fieldName;
			final String createGinFieldIndexQuery = "CREATE INDEX IF NOT EXISTS " + btreeIndexName + " ON " + tableName + " USING BTREE ((_source->>'" + fieldName + "'));";
			LOGGER.info(createGinFieldIndexQuery);
			PreparedStatement preparedStatement = connection.prepareStatement(createGinFieldIndexQuery);
			preparedStatement.execute();
			preparedStatement.close();
			break;
		}
	}

	private void internalCreatePsqlIndices(Connection connection, String tableName, IndexGenerationSettings indexGenerationSettings) throws SQLException {
		final String brinIndexName = IndexUtils.BRIN_INDEX_PREFIX + tableName;
		final String ginIndexName = IndexUtils.GIN_INDEX_PREFIX + tableName;

		PreparedStatement preparedStatement;

		if(indexGenerationSettings.getMode().equals(IndexGenerationMode.ALL)) {
			final String createGinIndexQuery = "CREATE INDEX IF NOT EXISTS " + ginIndexName + " ON " + tableName
					+ " USING GIN (_source jsonb_ops)";
			LOGGER.info(createGinIndexQuery);
			preparedStatement = connection.prepareStatement(createGinIndexQuery);
			preparedStatement.execute();
			preparedStatement.close();
		}

		final String createTimestampIndexQuery = "CREATE INDEX IF NOT EXISTS " + brinIndexName + " ON "
				+ tableName + " USING BRIN (_timestamp, _bucket1s, _bucket1m, _bucket1h, _bucket1d) WITH (pages_per_range = " + nodeSettingsService.getBrinPagesPerRange() + ")";
		LOGGER.info(createTimestampIndexQuery);
		preparedStatement = connection.prepareStatement(createTimestampIndexQuery);
		preparedStatement.execute();
		preparedStatement.close();
	}

	private class DelayedTableIndexCreation implements Delayed {
		public String tableName;
		public IndexGenerationSettings indexGenerationSettings;
		public long delayMillis;

		@Override
		public long getDelay(TimeUnit unit) {
			return unit.convert(delayMillis, TimeUnit.MILLISECONDS);
		}

		@Override
		public int compareTo(Delayed o) {
			return Long.compare(delayMillis, o.getDelay(TimeUnit.MILLISECONDS));
		}
	}

	private class DelayedFieldIndexCreation implements Delayed {
		public String tableName;
		public String fieldName;
		public IndexGenerationSettings indexGenerationSettings;
		public long delayMillis;

		@Override
		public long getDelay(TimeUnit unit) {
			return unit.convert(delayMillis, TimeUnit.MILLISECONDS);
		}

		@Override
		public int compareTo(Delayed o) {
			return Long.compare(delayMillis, o.getDelay(TimeUnit.MILLISECONDS));
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy