io.questdb.cairo.CairoEngine Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core Show documentation
Show all versions of core Show documentation
QuestDB is High Performance Time Series Database
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2020 QuestDB
*
* 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 io.questdb.cairo;
import io.questdb.cairo.pool.PoolListener;
import io.questdb.cairo.pool.ReaderPool;
import io.questdb.cairo.pool.WriterPool;
import io.questdb.cairo.sql.ReaderOutOfDateException;
import io.questdb.log.Log;
import io.questdb.log.LogFactory;
import io.questdb.mp.SynchronizedJob;
import io.questdb.std.Files;
import io.questdb.std.FilesFacade;
import io.questdb.std.Misc;
import io.questdb.std.Transient;
import io.questdb.std.microtime.MicrosecondClock;
import io.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 void releaseInactive() {
writerPool.releaseInactive();
readerPool.releaseInactive();
}
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
) {
return getReader(securityContext, tableName, TableUtils.ANY_TABLE_VERSION);
}
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("Could not lock '").put(tableName).put('\'');
}
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