/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * 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 VoltDB. If not, see <http://www.gnu.org/licenses/>. */ package org.voltdb; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; import org.voltcore.logging.Level; import org.voltcore.logging.VoltLogger; import org.voltdb.AuthSystem.AuthUser; import org.voltdb.catalog.Procedure; import org.voltdb.catalog.Table; import org.voltdb.client.BatchTimeoutOverrideType; import org.voltdb.client.ClientResponse; import org.voltdb.client.ProcedureCallback; import org.voltdb.utils.MiscUtils; import com.google_voltpatches.common.collect.ImmutableMap; /** * This class packs the parameters and dispatches the transactions. * Make sure responses over network thread does not touch this class. */ public class InternalConnectionHandler { final static String DEFAULT_INTERNAL_ADAPTER_NAME = "+!_InternalAdapter_!+"; public final static long SUPPRESS_INTERVAL = 60; private static final VoltLogger m_logger = new VoltLogger("InternalConnectionHandler"); // Atomically allows the catalog reference to change between access private final AtomicLong m_failedCount = new AtomicLong(); private final AtomicLong m_submitSuccessCount = new AtomicLong(); private volatile Map<Integer, InternalClientResponseAdapter> m_adapters = ImmutableMap.of(); // Synchronized in case multiple partitions are added concurrently. public synchronized void addAdapter(int pid, InternalClientResponseAdapter adapter) { final ImmutableMap.Builder<Integer, InternalClientResponseAdapter> builder = ImmutableMap.builder(); builder.putAll(m_adapters); builder.put(pid, adapter); m_adapters = builder.build(); } public boolean hasAdapter(int pid) { return m_adapters.containsKey(pid); } /** * Returns true if a table with the given name exists in the server catalog. */ public boolean hasTable(String name) { Table table = getCatalogContext().tables.get(name); return (table!=null); } public class NullCallback implements ProcedureCallback { @Override public void clientCallback(ClientResponse response) throws Exception { } } private CatalogContext getCatalogContext() { return VoltDB.instance().getCatalogContext(); } public boolean callProcedure( AuthUser user, boolean isAdmin, int timeout, ProcedureCallback cb, String procName, Object...args) { return callProcedure(user, isAdmin, timeout, cb, false, null, procName, args); } public boolean callProcedure( AuthUser user, boolean isAdmin, int timeout, ProcedureCallback cb, boolean ntPriority, Function<Integer, Boolean> backPressurePredicate, String procName, Object...args) { Procedure catProc = InvocationDispatcher.getProcedureFromName(procName, getCatalogContext()); if (catProc == null) { String fmt = "Cannot invoke procedure %s. Procedure not found."; m_logger.rateLimitedLog(SUPPRESS_INTERVAL, Level.ERROR, null, fmt, procName); m_failedCount.incrementAndGet(); return false; } StoredProcedureInvocation task = new StoredProcedureInvocation(); task.setProcName(procName); task.setParams(args); try { task = MiscUtils.roundTripForCL(task); } catch (Exception e) { String fmt = "Cannot invoke procedure %s. failed to create task."; m_logger.rateLimitedLog(SUPPRESS_INTERVAL, Level.ERROR, null, fmt, procName); m_failedCount.incrementAndGet(); return false; } if (timeout != BatchTimeoutOverrideType.NO_TIMEOUT) { task.setBatchTimeout(timeout); } int partition = -1; try { partition = InvocationDispatcher.getPartitionForProcedure(catProc, task); } catch (Exception e) { String fmt = "Can not invoke procedure %s. Partition not found."; m_logger.rateLimitedLog(SUPPRESS_INTERVAL, Level.ERROR, e, fmt, procName); m_failedCount.incrementAndGet(); return false; } final InternalClientResponseAdapter adapter = m_adapters.get(partition); InternalAdapterTaskAttributes kattrs = new InternalAdapterTaskAttributes( DEFAULT_INTERNAL_ADAPTER_NAME, isAdmin, adapter.connectionId()); if (!adapter.createTransaction(kattrs, procName, catProc, cb, null, task, user, partition, ntPriority, backPressurePredicate)) { m_failedCount.incrementAndGet(); return false; } m_submitSuccessCount.incrementAndGet(); return true; } // Use null backPressurePredicate for no back pressure public boolean callProcedure(InternalConnectionContext caller, Function<Integer, Boolean> backPressurePredicate, InternalConnectionStatsCollector statsCollector, ProcedureCallback procCallback, String proc, Object... fieldList) { Procedure catProc = InvocationDispatcher.getProcedureFromName(proc, getCatalogContext()); if (catProc == null) { String fmt = "Cannot invoke procedure %s from streaming interface %s. Procedure not found."; m_logger.rateLimitedLog(SUPPRESS_INTERVAL, Level.ERROR, null, fmt, proc, caller); m_failedCount.incrementAndGet(); return false; } StoredProcedureInvocation task = new StoredProcedureInvocation(); task.setProcName(proc); task.setParams(fieldList); try { task = MiscUtils.roundTripForCL(task); } catch (Exception e) { String fmt = "Cannot invoke procedure %s from streaming interface %s. failed to create task."; m_logger.rateLimitedLog(SUPPRESS_INTERVAL, Level.ERROR, null, fmt, proc, caller); m_failedCount.incrementAndGet(); return false; } int partition = -1; try { partition = InvocationDispatcher.getPartitionForProcedure(catProc, task); } catch (Exception e) { String fmt = "Can not invoke procedure %s from streaming interface %s. Partition not found."; m_logger.rateLimitedLog(SUPPRESS_INTERVAL, Level.ERROR, e, fmt, proc, caller); m_failedCount.incrementAndGet(); return false; } final InternalClientResponseAdapter adapter = m_adapters.get(partition); InternalAdapterTaskAttributes kattrs = new InternalAdapterTaskAttributes(caller, adapter.connectionId()); final AuthUser user = getCatalogContext().authSystem.getImporterUser(); if (!adapter.createTransaction(kattrs, proc, catProc, procCallback, statsCollector, task, user, partition, false, backPressurePredicate)) { m_failedCount.incrementAndGet(); return false; } m_submitSuccessCount.incrementAndGet(); return true; } }