
io.questdb.griffin.engine.table.AsyncGroupByNotKeyedAtom Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of questdb Show documentation
Show all versions of questdb Show documentation
QuestDB is high performance SQL time series database
/*******************************************************************************
* ___ _ ____ ____
* / _ \ _ _ ___ ___| |_| _ \| __ )
* | | | | | | |/ _ \/ __| __| | | | _ \
* | |_| | |_| | __/\__ \ |_| |_| | |_) |
* \__\_\\__,_|\___||___/\__|____/|____/
*
* Copyright (c) 2014-2019 Appsicle
* Copyright (c) 2019-2024 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.griffin.engine.table;
import io.questdb.cairo.CairoConfiguration;
import io.questdb.cairo.sql.Function;
import io.questdb.cairo.sql.SqlExecutionCircuitBreaker;
import io.questdb.cairo.sql.StatefulAtom;
import io.questdb.cairo.sql.SymbolTableSource;
import io.questdb.cairo.vm.api.MemoryCARW;
import io.questdb.griffin.PlanSink;
import io.questdb.griffin.Plannable;
import io.questdb.griffin.SqlException;
import io.questdb.griffin.SqlExecutionContext;
import io.questdb.griffin.engine.PerWorkerLocks;
import io.questdb.griffin.engine.functions.GroupByFunction;
import io.questdb.griffin.engine.groupby.GroupByAllocator;
import io.questdb.griffin.engine.groupby.GroupByAllocatorFactory;
import io.questdb.griffin.engine.groupby.GroupByFunctionsUpdater;
import io.questdb.griffin.engine.groupby.GroupByFunctionsUpdaterFactory;
import io.questdb.griffin.engine.groupby.GroupByUtils;
import io.questdb.griffin.engine.groupby.SimpleMapValue;
import io.questdb.jit.CompiledFilter;
import io.questdb.std.BytecodeAssembler;
import io.questdb.std.Misc;
import io.questdb.std.ObjList;
import io.questdb.std.Transient;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.Closeable;
import static io.questdb.griffin.engine.table.AsyncJitFilteredRecordCursorFactory.prepareBindVarMemory;
public class AsyncGroupByNotKeyedAtom implements StatefulAtom, Closeable, Plannable {
private final ObjList bindVarFunctions;
private final MemoryCARW bindVarMemory;
private final CompiledFilter compiledFilter;
private final GroupByAllocator ownerAllocator;
private final Function ownerFilter;
// Note: all function updaters should be used through a getFunctionUpdater() call
// to properly initialize group by functions' allocator.
private final GroupByFunctionsUpdater ownerFunctionUpdater;
private final ObjList ownerGroupByFunctions;
private final SimpleMapValue ownerMapValue;
private final ObjList perWorkerAllocators;
private final ObjList perWorkerFilters;
private final ObjList perWorkerFunctionUpdaters;
private final ObjList> perWorkerGroupByFunctions;
private final PerWorkerLocks perWorkerLocks;
private final ObjList perWorkerMapValues;
public AsyncGroupByNotKeyedAtom(
@Transient @NotNull BytecodeAssembler asm,
@NotNull CairoConfiguration configuration,
@NotNull ObjList ownerGroupByFunctions,
@Nullable ObjList> perWorkerGroupByFunctions,
int valueCount,
@Nullable CompiledFilter compiledFilter,
@Nullable MemoryCARW bindVarMemory,
@Nullable ObjList bindVarFunctions,
@Nullable Function ownerFilter,
@Nullable ObjList perWorkerFilters,
int workerCount
) {
assert perWorkerFilters == null || perWorkerFilters.size() == workerCount;
assert perWorkerGroupByFunctions == null || perWorkerGroupByFunctions.size() == workerCount;
final int slotCount = Math.min(workerCount, configuration.getPageFrameReduceQueueCapacity());
try {
this.compiledFilter = compiledFilter;
this.bindVarMemory = bindVarMemory;
this.bindVarFunctions = bindVarFunctions;
this.ownerFilter = ownerFilter;
this.perWorkerFilters = perWorkerFilters;
this.ownerGroupByFunctions = ownerGroupByFunctions;
this.perWorkerGroupByFunctions = perWorkerGroupByFunctions;
final Class updaterClass = GroupByFunctionsUpdaterFactory.getInstanceClass(asm, ownerGroupByFunctions.size());
ownerFunctionUpdater = GroupByFunctionsUpdaterFactory.getInstance(updaterClass, ownerGroupByFunctions);
if (perWorkerGroupByFunctions != null) {
perWorkerFunctionUpdaters = new ObjList<>(slotCount);
for (int i = 0; i < slotCount; i++) {
perWorkerFunctionUpdaters.extendAndSet(i, GroupByFunctionsUpdaterFactory.getInstance(updaterClass, perWorkerGroupByFunctions.getQuick(i)));
}
} else {
perWorkerFunctionUpdaters = null;
}
perWorkerLocks = new PerWorkerLocks(configuration, slotCount);
ownerMapValue = new SimpleMapValue(valueCount);
perWorkerMapValues = new ObjList<>(slotCount);
for (int i = 0; i < slotCount; i++) {
perWorkerMapValues.extendAndSet(i, new SimpleMapValue(valueCount));
}
ownerAllocator = GroupByAllocatorFactory.createAllocator(configuration);
perWorkerAllocators = new ObjList<>(slotCount);
for (int i = 0; i < slotCount; i++) {
perWorkerAllocators.extendAndSet(i, GroupByAllocatorFactory.createAllocator(configuration));
}
clear();
} catch (Throwable e) {
close();
throw e;
}
}
@Override
public void clear() {
// Make sure to set the allocator for the owner's group by functions.
// This is done by the getFunctionUpdater() method.
final GroupByFunctionsUpdater functionUpdater = getFunctionUpdater(-1);
functionUpdater.updateEmpty(ownerMapValue);
ownerMapValue.setNew(true);
for (int i = 0, n = perWorkerMapValues.size(); i < n; i++) {
SimpleMapValue value = perWorkerMapValues.getQuick(i);
functionUpdater.updateEmpty(value);
value.setNew(true);
}
if (perWorkerGroupByFunctions != null) {
for (int i = 0, n = perWorkerGroupByFunctions.size(); i < n; i++) {
Misc.clearObjList(perWorkerGroupByFunctions.getQuick(i));
}
}
Misc.free(ownerAllocator);
Misc.freeObjListAndKeepObjects(perWorkerAllocators);
}
@Override
public void close() {
Misc.free(compiledFilter);
Misc.free(bindVarMemory);
Misc.freeObjList(bindVarFunctions);
Misc.free(ownerFilter);
Misc.freeObjList(perWorkerFilters);
Misc.free(ownerAllocator);
Misc.freeObjList(perWorkerAllocators);
if (perWorkerGroupByFunctions != null) {
for (int i = 0, n = perWorkerGroupByFunctions.size(); i < n; i++) {
Misc.freeObjList(perWorkerGroupByFunctions.getQuick(i));
}
}
}
public ObjList getBindVarFunctions() {
return bindVarFunctions;
}
public MemoryCARW getBindVarMemory() {
return bindVarMemory;
}
public CompiledFilter getCompiledFilter() {
return compiledFilter;
}
public Function getFilter(int slotId) {
if (slotId == -1 || perWorkerFilters == null) {
return ownerFilter;
}
return perWorkerFilters.getQuick(slotId);
}
public GroupByFunctionsUpdater getFunctionUpdater(int slotId) {
if (slotId == -1 || perWorkerFunctionUpdaters == null) {
// Make sure to set worker-local allocator for the functions backed by the returned updater.
GroupByUtils.setAllocator(ownerGroupByFunctions, ownerAllocator);
return ownerFunctionUpdater;
}
GroupByUtils.setAllocator(perWorkerGroupByFunctions.getQuick(slotId), perWorkerAllocators.getQuick(slotId));
return perWorkerFunctionUpdaters.getQuick(slotId);
}
public SimpleMapValue getMapValue(int slotId) {
if (slotId == -1) {
return ownerMapValue;
}
return perWorkerMapValues.getQuick(slotId);
}
// Thread-unsafe, should be used by query owner thread only.
public SimpleMapValue getOwnerMapValue() {
return ownerMapValue;
}
// Thread-unsafe, should be used by query owner thread only.
public ObjList getPerWorkerMapValues() {
return perWorkerMapValues;
}
@Override
public void init(SymbolTableSource symbolTableSource, SqlExecutionContext executionContext) throws SqlException {
if (ownerFilter != null) {
ownerFilter.init(symbolTableSource, executionContext);
}
if (perWorkerFilters != null) {
final boolean current = executionContext.getCloneSymbolTables();
executionContext.setCloneSymbolTables(true);
try {
Function.init(perWorkerFilters, symbolTableSource, executionContext);
} finally {
executionContext.setCloneSymbolTables(current);
}
}
if (perWorkerGroupByFunctions != null) {
final boolean current = executionContext.getCloneSymbolTables();
executionContext.setCloneSymbolTables(true);
try {
for (int i = 0, n = perWorkerGroupByFunctions.size(); i < n; i++) {
Function.init(perWorkerGroupByFunctions.getQuick(i), symbolTableSource, executionContext);
}
} finally {
executionContext.setCloneSymbolTables(current);
}
}
if (bindVarFunctions != null) {
Function.init(bindVarFunctions, symbolTableSource, executionContext);
prepareBindVarMemory(executionContext, symbolTableSource, bindVarFunctions, bindVarMemory);
}
}
@Override
public void initCursor() {
if (ownerFilter != null) {
ownerFilter.initCursor();
}
if (perWorkerFilters != null) {
// Initialize all per-worker filters on the query owner thread to avoid
// DataUnavailableException thrown on worker threads when filtering.
Function.initCursor(perWorkerFilters);
}
}
public int maybeAcquire(int workerId, boolean owner, SqlExecutionCircuitBreaker circuitBreaker) {
if (workerId == -1 && owner) {
// Owner thread is free to use its own private filter, function updaters, etc. anytime.
return -1;
}
// All other threads, e.g. worker or work stealing threads, must always acquire a lock
// to use shared resources.
return perWorkerLocks.acquireSlot(workerId, circuitBreaker);
}
public void release(int slotId) {
perWorkerLocks.releaseSlot(slotId);
}
@Override
public void toPlan(PlanSink sink) {
sink.val(ownerFilter);
}
public void toTop() {
if (perWorkerGroupByFunctions != null) {
for (int i = 0, n = perWorkerGroupByFunctions.size(); i < n; i++) {
GroupByUtils.toTop(perWorkerGroupByFunctions.getQuick(i));
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy