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

io.questdb.cutlass.line.tcp.LineTcpNetworkIOJob Maven / Gradle / Ivy

/*******************************************************************************
 *     ___                  _   ____  ____
 *    / _ \ _   _  ___  ___| |_|  _ \| __ )
 *   | | | | | | |/ _ \/ __| __| | | |  _ \
 *   | |_| | |_| |  __/\__ \ |_| |_| | |_) |
 *    \__\_\\__,_|\___||___/\__|____/|____/
 *
 *  Copyright (c) 2014-2019 Appsicle
 *  Copyright (c) 2019-2023 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.cutlass.line.tcp;

import io.questdb.log.Log;
import io.questdb.log.LogFactory;
import io.questdb.network.IODispatcher;
import io.questdb.network.IOOperation;
import io.questdb.network.IORequestProcessor;
import io.questdb.std.*;
import io.questdb.std.datetime.millitime.MillisecondClock;
import io.questdb.std.str.DirectUtf8Sequence;
import io.questdb.std.str.Utf8String;
import org.jetbrains.annotations.NotNull;

import static io.questdb.network.IODispatcher.DISCONNECT_REASON_RETRY_FAILED;
import static io.questdb.network.IODispatcher.DISCONNECT_REASON_UNKNOWN_OPERATION;

class LineTcpNetworkIOJob implements NetworkIOJob {
    private final static Log LOG = LogFactory.getLog(LineTcpNetworkIOJob.class);
    private final IODispatcher dispatcher;
    private final long maintenanceInterval;
    private final MillisecondClock millisecondClock;
    private final LineTcpMeasurementScheduler scheduler;
    private final Utf8StringObjHashMap tableUpdateDetailsUtf8 = new Utf8StringObjHashMap<>();
    private final WeakClosableObjectPool unusedSymbolCaches;
    private final int workerId;
    // Context blocked on LineTcpMeasurementScheduler queue
    private LineTcpConnectionContext busyContext = null;
    private final IORequestProcessor onRequest = this::onRequest;
    private long maintenanceJobDeadline;

    LineTcpNetworkIOJob(
            LineTcpReceiverConfiguration configuration,
            LineTcpMeasurementScheduler scheduler,
            IODispatcher dispatcher,
            int workerId
    ) {
        this.millisecondClock = configuration.getMillisecondClock();
        this.maintenanceInterval = configuration.getMaintenanceInterval();
        this.scheduler = scheduler;
        this.maintenanceJobDeadline = millisecondClock.getTicks() + maintenanceInterval;
        this.dispatcher = dispatcher;
        this.workerId = workerId;
        this.unusedSymbolCaches = new WeakClosableObjectPool<>(() -> new SymbolCache(configuration), 10, true);
    }

    @Override
    public void addTableUpdateDetails(Utf8String tableNameUtf8, TableUpdateDetails tableUpdateDetails) {
        tableUpdateDetailsUtf8.put(tableNameUtf8, tableUpdateDetails);
        tableUpdateDetails.addReference(workerId);
    }

    @Override
    public void close() {
        if (busyContext != null) {
            busyContext.getDispatcher().disconnect(busyContext, DISCONNECT_REASON_RETRY_FAILED);
            busyContext = null;
        }
        Misc.free(unusedSymbolCaches);
    }

    @Override
    public TableUpdateDetails getLocalTableDetails(DirectUtf8Sequence tableNameUtf8) {
        return tableUpdateDetailsUtf8.get(tableNameUtf8);
    }

    @Override
    public Pool getSymbolCachePool() {
        return unusedSymbolCaches;
    }

    @Override
    public int getWorkerId() {
        return workerId;
    }

    @Override
    public void releaseWalTableDetails() {
        scheduler.releaseWalTableDetails(tableUpdateDetailsUtf8);
    }

    @Override
    public boolean run(int workerId, @NotNull RunStatus runStatus) {
        assert this.workerId == workerId;
        boolean busy = false;
        if (busyContext != null) {
            if (handleIO(busyContext)) {
                // queue is still full
                return true;
            }
            LOG.debug().$("context is no longer waiting on a full queue [fd=").$(busyContext.getFd()).$(']').$();
            busyContext = null;
            busy = true;
        }

        if (dispatcher.processIOQueue(onRequest)) {
            busy = true;
        }

        final long millis = millisecondClock.getTicks();
        if (millis > maintenanceJobDeadline) {
            busy = scheduler.doMaintenance(tableUpdateDetailsUtf8, workerId, millis);
            if (!busy) {
                maintenanceJobDeadline = millis + maintenanceInterval;
            }
        }

        return busy;
    }

    private boolean handleIO(LineTcpConnectionContext context) {
        if (!context.invalid()) {
            switch (context.handleIO(this)) {
                case NEEDS_READ:
                    context.getDispatcher().registerChannel(context, IOOperation.READ);
                    return false;
                case NEEDS_WRITE:
                    context.getDispatcher().registerChannel(context, IOOperation.WRITE);
                    return false;
                case QUEUE_FULL:
                    return true;
                case NEEDS_DISCONNECT:
                    context.getDispatcher().disconnect(context, DISCONNECT_REASON_UNKNOWN_OPERATION);
                    return false;
            }
        }
        return false;
    }

    private boolean onRequest(int operation, LineTcpConnectionContext context) {
        if (operation == IOOperation.HEARTBEAT) {
            context.doMaintenance(millisecondClock.getTicks());
            context.getDispatcher().registerChannel(context, IOOperation.HEARTBEAT);
            return false;
        }
        if (handleIO(context)) {
            busyContext = context;
            LOG.debug().$("context is waiting on a full queue [fd=").$(context.getFd()).$(']').$();
            return false;
        }
        return true;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy