
com.questdb.cairo.CairoEngine Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of questdb-core Show documentation
Show all versions of questdb-core Show documentation
QuestDB is High Performance Time Series Database
The newest version!
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (C) 2014-2019 Appsicle
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
******************************************************************************/
package com.questdb.cairo;
import com.questdb.cairo.pool.PoolListener;
import com.questdb.cairo.pool.ReaderPool;
import com.questdb.cairo.pool.WriterPool;
import com.questdb.log.Log;
import com.questdb.log.LogFactory;
import com.questdb.mp.SynchronizedJob;
import com.questdb.std.Files;
import com.questdb.std.FilesFacade;
import com.questdb.std.Misc;
import com.questdb.std.Transient;
import com.questdb.std.microtime.MicrosecondClock;
import com.questdb.std.str.Path;
import org.jetbrains.annotations.Nullable;
import java.io.Closeable;
public class CairoEngine implements Closeable {
private static final Log LOG = LogFactory.getLog(CairoEngine.class);
private final WriterPool writerPool;
private final ReaderPool readerPool;
private final CairoConfiguration configuration;
public CairoEngine(CairoConfiguration configuration) {
this(configuration, null);
}
public CairoEngine(CairoConfiguration configuration, CairoWorkScheduler workScheduler) {
this.configuration = configuration;
this.writerPool = new WriterPool(configuration, workScheduler);
this.readerPool = new ReaderPool(configuration);
if (workScheduler != null) {
workScheduler.addJob(new WriterMaintenanceJob(configuration));
workScheduler.addJob(new ColumnIndexerJob(workScheduler));
}
}
@Override
public void close() {
Misc.free(writerPool);
Misc.free(readerPool);
}
public void creatTable(
CairoSecurityContext securityContext,
AppendMemory mem,
Path path,
TableStructure struct
) {
TableUtils.createTable(
configuration.getFilesFacade(),
mem,
path,
configuration.getRoot(),
struct,
configuration.getMkDirMode()
);
}
public int getBusyReaderCount() {
return readerPool.getBusyCount();
}
public int getBusyWriterCount() {
return writerPool.getBusyCount();
}
public CairoConfiguration getConfiguration() {
return configuration;
}
public PoolListener getPoolListener() {
return this.writerPool.getPoolListener();
}
public void setPoolListener(PoolListener poolListener) {
this.writerPool.setPoolListener(poolListener);
this.readerPool.setPoolListener(poolListener);
}
public TableReader getReader(
CairoSecurityContext securityContext,
CharSequence tableName,
long version
) {
TableReader reader = readerPool.get(tableName);
if (version > -1 && reader.getVersion() != version) {
reader.close();
throw ReaderOutOfDateException.INSTANCE;
}
return reader;
}
public int getStatus(
CairoSecurityContext securityContext,
Path path,
CharSequence tableName,
int lo,
int hi
) {
return TableUtils.exists(configuration.getFilesFacade(), path, configuration.getRoot(), tableName, lo, hi);
}
public int getStatus(
CairoSecurityContext securityContext,
Path path,
CharSequence tableName
) {
return getStatus(securityContext, path, tableName, 0, tableName.length());
}
public TableWriter getWriter(
CairoSecurityContext securityContext,
CharSequence tableName
) {
return writerPool.get(tableName);
}
public boolean lock(
CairoSecurityContext securityContext,
CharSequence tableName
) {
if (writerPool.lock(tableName)) {
boolean locked = readerPool.lock(tableName);
if (locked) {
return true;
}
writerPool.unlock(tableName);
}
return false;
}
public boolean releaseAllReaders() {
return readerPool.releaseAll();
}
public boolean lockReaders(CharSequence tableName) {
return readerPool.lock(tableName);
}
public void unlockReaders(CharSequence tableName) {
readerPool.unlock(tableName);
}
public boolean releaseAllWriters() {
return writerPool.releaseAll();
}
public void remove(
CairoSecurityContext securityContext,
Path path,
CharSequence tableName
) {
if (lock(securityContext, tableName)) {
try {
path.of(configuration.getRoot()).concat(tableName).$();
if (!configuration.getFilesFacade().rmdir(path)) {
int error = configuration.getFilesFacade().errno();
LOG.error().$("remove failed [tableName='").utf8(tableName).$("', error=").$(error).$(']').$();
throw CairoException.instance(error).put("Table remove failed");
}
return;
} finally {
unlock(securityContext, tableName, null);
}
}
throw CairoException.instance(configuration.getFilesFacade().errno()).put("Cannot lock ").put(tableName);
}
public boolean removeDirectory(@Transient Path path, CharSequence dir) {
path.of(configuration.getRoot()).concat(dir);
final FilesFacade ff = configuration.getFilesFacade();
return ff.rmdir(path.put(Files.SEPARATOR).$());
}
public void rename(
CairoSecurityContext securityContext,
Path path,
CharSequence tableName,
Path otherPath,
String newName
) {
if (lock(securityContext, tableName)) {
try {
rename0(path, tableName, otherPath, newName);
} finally {
unlock(securityContext, tableName, null);
}
} else {
LOG.error().$("cannot lock and rename [from='").$(tableName).$("', to='").$(newName).$("']").$();
throw CairoException.instance(0).put("Cannot lock [table=").put(tableName).put(']');
}
}
public void unlock(
CairoSecurityContext securityContext,
CharSequence tableName,
@Nullable TableWriter writer
) {
readerPool.unlock(tableName);
writerPool.unlock(tableName, writer);
}
private void rename0(Path path, CharSequence tableName, Path otherPath, CharSequence to) {
final FilesFacade ff = configuration.getFilesFacade();
final CharSequence root = configuration.getRoot();
if (TableUtils.exists(ff, path, root, tableName) != TableUtils.TABLE_EXISTS) {
LOG.error().$('\'').utf8(tableName).$("' does not exist. Rename failed.").$();
throw CairoException.instance(0).put("Rename failed. Table '").put(tableName).put("' does not exist");
}
path.of(root).concat(tableName).$();
otherPath.of(root).concat(to).$();
if (ff.exists(otherPath)) {
LOG.error().$("rename target exists [from='").$(tableName).$("', to='").$(otherPath).$("']").$();
throw CairoException.instance(0).put("Rename target exists");
}
if (!ff.rename(path, otherPath)) {
int error = ff.errno();
LOG.error().$("rename failed [from='").$(path).$("', to='").$(otherPath).$("', error=").$(error).$(']').$();
throw CairoException.instance(error).put("Rename failed");
}
}
private class WriterMaintenanceJob extends SynchronizedJob {
private final MicrosecondClock clock;
private final long checkInterval;
private long last = 0;
public WriterMaintenanceJob(CairoConfiguration configuration) {
this.clock = configuration.getMicrosecondClock();
this.checkInterval = configuration.getIdleCheckInterval() * 1000;
}
protected boolean doRun() {
boolean w = writerPool.releaseInactive();
boolean r = readerPool.releaseInactive();
return w || r;
}
@Override
protected boolean runSerially() {
long t = clock.getTicks();
if (last + checkInterval < t) {
last = t;
return doRun();
}
return false;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy