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

is.codion.common.db.pool.DefaultConnectionPoolCounter Maven / Gradle / Ivy

/*
 * This file is part of Codion.
 *
 * Codion is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Codion is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Codion.  If not, see .
 *
 * Copyright (c) 2013 - 2024, Björn Darri Sigurðsson.
 */
package is.codion.common.db.pool;

import is.codion.common.scheduler.TaskScheduler;

import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.IntStream;

import static java.util.stream.Collectors.toList;

/**
 * A connection pool statistics collector.
 */
final class DefaultConnectionPoolCounter {

	private static final double THOUSAND = 1000d;
	private static final int SNAPSHOT_STATS_SIZE = 1000;
	private static final int SNAPSHOT_COLLECTION_INTERVAL_MS = 10;
	private static final int CHECK_OUT_TIMES_MAX_SIZE = 10000;

	private final AbstractConnectionPoolWrapper connectionPool;
	private final long creationDate = System.currentTimeMillis();
	private final LinkedList checkOutTimes = new LinkedList<>();
	private final LinkedList snapshotStatistics = new LinkedList<>();
	private volatile boolean collectSnapshotStatistics = false;
	private volatile boolean collectCheckOutTimes = false;
	private final TaskScheduler snapshotStatisticsCollector = TaskScheduler.builder(new StatisticsCollector())
					.interval(SNAPSHOT_COLLECTION_INTERVAL_MS, TimeUnit.MILLISECONDS)
					.build();

	private final AtomicLong resetDate = new AtomicLong(creationDate);
	private final AtomicLong requestsPerSecondTime = new AtomicLong(creationDate);
	private final AtomicInteger connectionsCreated = new AtomicInteger();
	private final AtomicInteger connectionsDestroyed = new AtomicInteger();
	private final AtomicInteger connectionRequests = new AtomicInteger();
	private final AtomicInteger requestsPerSecondCounter = new AtomicInteger();
	private final AtomicInteger connectionRequestsFailed = new AtomicInteger();
	private final AtomicInteger requestsFailedPerSecondCounter = new AtomicInteger();

	DefaultConnectionPoolCounter(AbstractConnectionPoolWrapper connectionPool) {
		this.connectionPool = connectionPool;
	}

	boolean isCollectSnapshotStatistics() {
		return collectSnapshotStatistics;
	}

	void setCollectSnapshotStatistics(boolean collectSnapshotStatistics) {
		synchronized (snapshotStatistics) {
			if (collectSnapshotStatistics) {
				IntStream.range(0, SNAPSHOT_STATS_SIZE).forEach(i -> snapshotStatistics.add(new DefaultConnectionPoolState()));
				snapshotStatisticsCollector.start();
			}
			else {
				snapshotStatisticsCollector.stop();
				snapshotStatistics.clear();
			}
			this.collectSnapshotStatistics = collectSnapshotStatistics;
		}
	}

	boolean isCollectCheckOutTimes() {
		return collectCheckOutTimes;
	}

	void setCollectCheckOutTimes(boolean collectCheckOutTimes) {
		synchronized (checkOutTimes) {
			if (!collectCheckOutTimes) {
				checkOutTimes.clear();
			}
			this.collectCheckOutTimes = collectCheckOutTimes;
		}
	}

	void addCheckOutTime(int time) {
		if (collectCheckOutTimes) {
			synchronized (checkOutTimes) {
				checkOutTimes.add(time);
				if (checkOutTimes.size() > CHECK_OUT_TIMES_MAX_SIZE) {
					checkOutTimes.removeFirst();
				}
			}
		}
	}

	void incrementConnectionsDestroyedCounter() {
		connectionsDestroyed.incrementAndGet();
	}

	void incrementConnectionsCreatedCounter() {
		connectionsCreated.incrementAndGet();
	}

	void incrementFailedRequestCounter() {
		connectionRequestsFailed.incrementAndGet();
		requestsFailedPerSecondCounter.incrementAndGet();
	}

	void incrementRequestCounter() {
		connectionRequests.incrementAndGet();
		requestsPerSecondCounter.incrementAndGet();
	}

	void resetStatistics() {
		connectionsCreated.set(0);
		connectionsDestroyed.set(0);
		connectionRequests.set(0);
		connectionRequestsFailed.set(0);
		synchronized (checkOutTimes) {
			checkOutTimes.clear();
		}
		resetDate.set(System.currentTimeMillis());
	}

	ConnectionPoolStatistics collectStatistics(long since) {
		DefaultConnectionPoolStatistics statistics = new DefaultConnectionPoolStatistics(connectionPool.user().username());
		long current = System.currentTimeMillis();
		statistics.timestamp(current);
		statistics.resetDate(resetDate.get());
		statistics.availableInPool(connectionPool.available());
		statistics.connectionsInUse(connectionPool.inUse());
		statistics.connectionsCreated(connectionsCreated.get());
		statistics.connectionsDestroyed(connectionsDestroyed.get());
		statistics.creationDate(creationDate);
		statistics.connectionRequests(connectionRequests.get());
		statistics.connectionRequestsFailed(connectionRequestsFailed.get());
		double seconds = (current - requestsPerSecondTime.get()) / THOUSAND;
		requestsPerSecondTime.set(current);
		statistics.requestsPerSecond((int) (requestsPerSecondCounter.get() / seconds));
		requestsPerSecondCounter.set(0);
		statistics.requestsFailedPerSecond((int) (requestsFailedPerSecondCounter.get() / seconds));
		requestsFailedPerSecondCounter.set(0);
		if (!checkOutTimes.isEmpty()) {
			populateCheckOutTime(statistics);
		}
		if (collectSnapshotStatistics && since >= 0) {
			synchronized (snapshotStatistics) {
				statistics.snapshot(snapshotStatistics.stream()
								.filter(state -> state.timestamp() >= since)
								.collect(toList()));
			}
		}

		return statistics;
	}

	private void populateCheckOutTime(DefaultConnectionPoolStatistics statistics) {
		int total = 0;
		int min = -1;
		int max = -1;
		synchronized (checkOutTimes) {
			for (Integer time : checkOutTimes) {
				total += time;
				if (min == -1) {
					min = time;
					max = time;
				}
				else {
					min = Math.min(min, time);
					max = Math.max(max, time);
				}
			}
			statistics.averageCheckOutTime(total / checkOutTimes.size());
			statistics.minimumCheckOutTime(min);
			statistics.maximumCheckOutTime(max);
			checkOutTimes.clear();
		}
	}

	private final class StatisticsCollector implements Runnable {

		/**
		 * Adds the current state of the pool to the snapshot connection pool log
		 */
		@Override
		public void run() {
			synchronized (snapshotStatistics) {
				snapshotStatistics.addLast(connectionPool.updateState(snapshotStatistics.removeFirst()));
			}
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy