/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.dqp.internal.process;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import javax.transaction.xa.Xid;
import org.teiid.PreParser;
import org.teiid.adminapi.AdminException;
import org.teiid.adminapi.Request.ProcessingState;
import org.teiid.adminapi.Request.ThreadState;
import org.teiid.adminapi.VDB.Status;
import org.teiid.adminapi.impl.RequestMetadata;
import org.teiid.adminapi.impl.SessionMetadata;
import org.teiid.adminapi.impl.TransactionMetadata;
import org.teiid.adminapi.impl.VDBMetaData;
import org.teiid.adminapi.impl.WorkerPoolStatisticsMetadata;
import org.teiid.client.DQP;
import org.teiid.client.RequestMessage;
import org.teiid.client.RequestMessage.StatementType;
import org.teiid.client.ResultsMessage;
import org.teiid.client.lob.LobChunk;
import org.teiid.client.metadata.MetadataResult;
import org.teiid.client.plan.PlanNode;
import org.teiid.client.util.ResultsFuture;
import org.teiid.client.util.ResultsReceiver;
import org.teiid.client.xa.XATransactionException;
import org.teiid.client.xa.XidImpl;
import org.teiid.common.buffer.BufferManager;
import org.teiid.core.TeiidComponentException;
import org.teiid.core.TeiidProcessingException;
import org.teiid.core.TeiidRuntimeException;
import org.teiid.core.types.Streamable;
import org.teiid.core.util.ApplicationInfo;
import org.teiid.core.util.ExecutorUtils;
import org.teiid.core.util.PropertiesUtils;
import org.teiid.dqp.message.AtomicRequestMessage;
import org.teiid.dqp.message.RequestID;
import org.teiid.dqp.service.TransactionContext;
import org.teiid.dqp.service.TransactionContext.Scope;
import org.teiid.dqp.service.TransactionService;
import org.teiid.events.EventDistributor;
import org.teiid.jdbc.EnhancedTimer;
import org.teiid.jdbc.LocalProfile;
import org.teiid.logging.CommandLogMessage;
import org.teiid.logging.CommandLogMessage.Event;
import org.teiid.logging.LogConstants;
import org.teiid.logging.LogManager;
import org.teiid.logging.MessageLevel;
import org.teiid.query.QueryPlugin;
import org.teiid.query.processor.QueryProcessor;
import org.teiid.query.tempdata.GlobalTableStoreImpl;
import org.teiid.query.tempdata.TempTableDataManager;
import org.teiid.query.tempdata.TempTableStore;
import org.teiid.query.tempdata.TempTableStore.TransactionMode;
import org.teiid.query.util.CommandContext;
import org.teiid.query.util.Options;
/**
* Implements the core DQP processing.
*/
public class DQPCore implements DQP {
public interface CompletionListener<T> {
void onCompletion(FutureWork<T> future);
}
private TeiidExecutor processWorkerPool;
// Resources
private BufferManager bufferManager;
private TempTableDataManager dataTierMgr;
private SessionAwareCache<PreparedPlan> prepPlanCache;
private SessionAwareCache<CachedResults> rsCache;
private TransactionService transactionService;
private EventDistributor eventDistributor;
private DQPConfiguration config = new DQPConfiguration();
private int chunkSize = Streamable.STREAMING_BATCH_SIZE_IN_BYTES;
private Map<RequestID, RequestWorkItem> requests = new ConcurrentHashMap<RequestID, RequestWorkItem>();
private Map<String, ClientState> clientState = new ConcurrentHashMap<String, ClientState>();
private int maxActivePlans = DQPConfiguration.DEFAULT_MAX_ACTIVE_PLANS;
private int currentlyActivePlans;
private int userRequestSourceConcurrency;
private LinkedList<RequestWorkItem> waitingPlans = new LinkedList<RequestWorkItem>();
private int maxWaitingPlans = 0;
private AuthorizationValidator authorizationValidator;
private EnhancedTimer cancellationTimer;
private Options options;
private ExecutorService timeoutExecutor;
private LocalProfile localProfile;
private volatile boolean shutdown;
/**
* perform a full shutdown and wait for 10 seconds for all threads to finish
*/
public void stop() {
shutdown = true;
for (RequestID request : requests.keySet()) {
try {
cancelRequest(request);
} catch (TeiidComponentException e) {
}
}
processWorkerPool.shutdownNow();
try {
processWorkerPool.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
}
this.timeoutExecutor.shutdownNow();
try {
timeoutExecutor.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
}
// TODO: Should we be doing more cleanup here??
LogManager.logDetail(LogConstants.CTX_DQP, "Stopping the DQP"); //$NON-NLS-1$
}
/**
* Return a list of {@link RequestMetadata} for the given session
*/
public List<RequestMetadata> getRequestsForSession(String sessionId) {
ClientState state = getClientState(sessionId, false);
if (state == null) {
return Collections.emptyList();
}
return buildRequestInfos(state.getRequests(), -1);
}
public ClientState getClientState(String key, boolean create) {
if (key == null) {
return null;
}
ClientState state = clientState.get(key);
if (state == null && create) {
state = new ClientState(new TempTableStore(key, TransactionMode.ISOLATE_WRITES));
clientState.put(key, state);
}
return state;
}
/**
* Return a list of all {@link RequestMetadata}
*/
public List<RequestMetadata> getRequests() {
return buildRequestInfos(requests.keySet(), -1);
}
public List<RequestMetadata> getLongRunningRequests(){
return buildRequestInfos(requests.keySet(), System.currentTimeMillis() - this.config.getQueryThresholdInMilli() );
}
private List<RequestMetadata> buildRequestInfos(Collection<RequestID> ids, long longRunningQueryThreshold) {
List<RequestMetadata> results = new ArrayList<RequestMetadata>();
for (RequestID requestID : ids) {
RequestWorkItem holder = requests.get(requestID);
if(holder != null && !holder.isCanceled() && (longRunningQueryThreshold == -1 || holder.getProcessingTimestamp() < longRunningQueryThreshold)) {
RequestMetadata req = new RequestMetadata();
req.setExecutionId(holder.requestID.getExecutionID());
req.setSessionId(holder.requestID.getConnectionID());
req.setCommand(holder.requestMsg.getCommandString());
req.setStartTime(holder.getProcessingTimestamp());
req.setState(holder.isCanceled()?ProcessingState.CANCELED:(holder.isDoneProcessing() || holder.isCloseRequested())?ProcessingState.DONE:ProcessingState.PROCESSING);
switch (holder.getThreadState()) {
case DONE:
case IDLE:
req.setThreadState(ThreadState.IDLE);
break;
default:
if (holder.isProcessing()) {
req.setThreadState(ThreadState.RUNNING);
} else {
req.setThreadState(ThreadState.QUEUED);
}
}
if (holder.getTransactionContext() != null && holder.getTransactionContext().getTransactionType() != Scope.NONE) {
req.setTransactionId(holder.getTransactionContext().getTransactionId());
}
for (DataTierTupleSource conInfo : holder.getConnectorRequests()) {
String connectorName = conInfo.getConnectorName();
if (connectorName == null) {
continue;
}
// If the request has not yet completed processing, then
// add all the subrequest messages
AtomicRequestMessage arm = conInfo.getAtomicRequestMessage();
RequestMetadata info = new RequestMetadata();
if (conInfo.isQueued()) {
info.setThreadState(ThreadState.QUEUED);
} else if (conInfo.isRunning()) {
info.setThreadState(ThreadState.RUNNING);
} else {
info.setThreadState(ThreadState.IDLE);
}
info.setExecutionId(arm.getRequestID().getExecutionID());
info.setSessionId(holder.requestID.getConnectionID());
info.setCommand(arm.getCommand().toString());
info.setStartTime(arm.getProcessingTimestamp());
info.setSourceRequest(true);
info.setNodeId(arm.getAtomicRequestID().getNodeID());
info.setState(conInfo.isCanceled()?ProcessingState.CANCELED:conInfo.isDone()?ProcessingState.DONE:ProcessingState.PROCESSING);
results.add(info);
}
results.add(req);
}
}
return results;
}
@Override
public ResultsFuture<ResultsMessage> executeRequest(long reqID,RequestMessage requestMsg) throws TeiidProcessingException {
return executeRequest(reqID, requestMsg, null);
}
public ResultsFuture<ResultsMessage> executeRequest(long reqID,RequestMessage requestMsg, Long queryTimeout) throws TeiidProcessingException {
DQPWorkContext workContext = DQPWorkContext.getWorkContext();
checkActive(workContext);
RequestID requestID = workContext.getRequestID(reqID);
requestMsg.setFetchSize(Math.min(requestMsg.getFetchSize(), this.config.getMaxRowsFetchSize()));
Request request = null;
if ( requestMsg.isPreparedStatement() || requestMsg.isCallableStatement() || requestMsg.getRequestOptions().isContinuous()) {
request = new PreparedStatementRequest(prepPlanCache);
} else {
request = new Request();
}
ClientState state = this.getClientState(workContext.getSessionId(), true);
if (state.session == null) {
state.session = workContext.getSession();
}
request.initialize(requestMsg, bufferManager,
dataTierMgr, transactionService, state.sessionTables,
workContext, this.prepPlanCache);
request.setOptions(options);
request.setExecutor(this.processWorkerPool);
request.setResultSetCacheEnabled(this.rsCache != null);
request.setAuthorizationValidator(this.authorizationValidator);
final PreParser preparser = workContext.getVDB().getAttachment(PreParser.class);
if (preparser != null) {
if (this.config.getPreParser() != null) {
//chain the preparsing effect
request.setPreParser(new PreParser() {
@Override
public String preParse(String command, org.teiid.CommandContext context) {
String preParse = config.getPreParser().preParse(command, context);
return preparser.preParse(preParse, context);
}
});
} else {
request.setPreParser(preparser);
}
} else {
request.setPreParser(this.config.getPreParser());
}
request.setUserRequestConcurrency(this.getUserRequestSourceConcurrency());
ResultsFuture<ResultsMessage> resultsFuture = new ResultsFuture<ResultsMessage>();
final RequestWorkItem workItem = new RequestWorkItem(this, requestMsg, request, resultsFuture.getResultsReceiver(), requestID, workContext);
logMMCommand(workItem, Event.NEW, null, null);
addRequest(requestID, workItem, state);
long timeout = workContext.getVDB().getQueryTimeout();
timeout = Math.min(timeout>0?timeout:Long.MAX_VALUE, config.getQueryTimeout()>0?config.getQueryTimeout():Long.MAX_VALUE);
if (queryTimeout != null && queryTimeout > 0) {
timeout = Math.min(timeout>0?timeout:Long.MAX_VALUE, queryTimeout);
}
if (timeout < Long.MAX_VALUE) {
final long finalTimeout = timeout;
workItem.setCancelTask(this.cancellationTimer.add(new Runnable() {
WeakReference<RequestWorkItem> workItemRef = new WeakReference<RequestWorkItem>(workItem);
@Override
public void run() {
try {
RequestWorkItem wi = workItemRef.get();
if (wi != null) {
LogManager.logInfo(LogConstants.CTX_DQP, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31096, wi.requestID, finalTimeout));
wi.requestCancel();
}
} catch (TeiidComponentException e) {
LogManager.logError(LogConstants.CTX_DQP, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30018));
}
}
}, timeout));
}
boolean runInThread = requestMsg.isSync();
synchronized (waitingPlans) {
if (runInThread || currentlyActivePlans <= maxActivePlans) {
startActivePlan(workItem, !runInThread);
} else {
if (LogManager.isMessageToBeRecorded(LogConstants.CTX_DQP, MessageLevel.DETAIL)) {
LogManager.logDetail(LogConstants.CTX_DQP, workItem.requestID, "Queuing plan, since max plans has been reached."); //$NON-NLS-1$
}
waitingPlans.add(workItem);
maxWaitingPlans = Math.max(this.maxWaitingPlans, waitingPlans.size());
}
}
if (runInThread) {
workItem.useCallingThread = true;
workItem.run();
}
return resultsFuture;
}
public ResultsFuture<ResultsMessage> processCursorRequest(long reqID,
int batchFirst, int fetchSize) throws TeiidProcessingException {
if (LogManager.isMessageToBeRecorded(LogConstants.CTX_DQP, MessageLevel.DETAIL)) {
LogManager.logDetail(LogConstants.CTX_DQP, "DQP process cursor request", batchFirst, fetchSize); //$NON-NLS-1$
}
DQPWorkContext workContext = DQPWorkContext.getWorkContext();
ResultsFuture<ResultsMessage> resultsFuture = new ResultsFuture<ResultsMessage>();
RequestWorkItem workItem = getRequestWorkItem(workContext.getRequestID(reqID));
workItem.requestMore(batchFirst, batchFirst + Math.min(fetchSize, this.config.getMaxRowsFetchSize()) - 1, resultsFuture.getResultsReceiver());
return resultsFuture;
}
void addRequest(RequestID requestID, RequestWorkItem workItem, ClientState state) {
RequestWorkItem item = this.requests.put(requestID, workItem);
if (item != null) {
this.requests.put(requestID, item);
throw new TeiidRuntimeException("duplicate request id " + requestID); //$NON-NLS-1$
}
state.addRequest(requestID);
}
private void startActivePlan(RequestWorkItem workItem, boolean addToQueue) {
boolean continuous = workItem.requestMsg.getRequestOptions().isContinuous();
workItem.active = !continuous;
if (addToQueue) {
this.addWork(workItem);
}
if (!continuous) {
this.currentlyActivePlans++;
}
}
void finishProcessing(final RequestWorkItem workItem) {
synchronized (waitingPlans) {
if (!workItem.active) {
return;
}
workItem.active = false;
currentlyActivePlans--;
if (!waitingPlans.isEmpty()) {
RequestWorkItem work = waitingPlans.remove();
startActivePlan(work, true);
}
}
if (LogManager.isMessageToBeRecorded(LogConstants.CTX_DQP, MessageLevel.DETAIL)) {
LogManager.logDetail(LogConstants.CTX_DQP, workItem.requestID, "Finished Processing"); //$NON-NLS-1$
}
}
public int getActivePlanCount() {
return this.currentlyActivePlans;
}
public boolean blockOnOutputBuffer(RequestWorkItem item) {
synchronized (waitingPlans) {
if (!waitingPlans.isEmpty()) {
return false;
}
if (item.useCallingThread || item.getDqpWorkContext().getSession().isEmbedded()) {
return false;
}
}
return true;
}
public int getWaitingPlanCount() {
return waitingPlans.size();
}
public int getMaxWaitingPlanWatermark() {
return this.maxWaitingPlans;
}
void removeRequest(final RequestWorkItem workItem) {
finishProcessing(workItem);
this.requests.remove(workItem.requestID);
ClientState state = getClientState(workItem.getDqpWorkContext().getSessionId(), false);
if (state != null) {
state.removeRequest(workItem.requestID);
}
}
void addWork(Runnable work) {
try {
this.processWorkerPool.execute(work);
} catch (RejectedExecutionException e) {
if (!shutdown) {
throw e;
}
LogManager.logDetail(LogConstants.CTX_DQP, e, "In the process of shutting down, work will not be started"); //$NON-NLS-1$
}
}
Future<Void> scheduleWork(final Runnable r, long delay) {
return this.cancellationTimer.add(r, delay);
}
public ResultsFuture<?> closeLobChunkStream(int lobRequestId,
long requestId, String streamId)
throws TeiidProcessingException {
if (LogManager.isMessageToBeRecorded(LogConstants.CTX_DQP, MessageLevel.DETAIL)) {
LogManager.logDetail(LogConstants.CTX_DQP, "Request to close the Lob stream with Stream id="+streamId+" instance id="+lobRequestId); //$NON-NLS-1$//$NON-NLS-2$
}
DQPWorkContext workContext = DQPWorkContext.getWorkContext();
RequestWorkItem workItem = safeGetWorkItem(workContext.getRequestID(requestId));
if (workItem != null) {
workItem.removeLobStream(lobRequestId);
}
return ResultsFuture.NULL_FUTURE;
}
public ResultsFuture<LobChunk> requestNextLobChunk(int lobRequestId,
long requestId, String streamId)
throws TeiidProcessingException {
if (LogManager.isMessageToBeRecorded(LogConstants.CTX_DQP, MessageLevel.DETAIL)) {
LogManager.logDetail(LogConstants.CTX_DQP, "Request for next Lob chunk with Stream id="+streamId+" instance id="+lobRequestId); //$NON-NLS-1$//$NON-NLS-2$
}
RequestWorkItem workItem = getRequestWorkItem(DQPWorkContext.getWorkContext().getRequestID(requestId));
ResultsFuture<LobChunk> resultsFuture = new ResultsFuture<LobChunk>();
workItem.processLobChunkRequest(streamId, lobRequestId, resultsFuture.getResultsReceiver());
return resultsFuture;
}
RequestWorkItem getRequestWorkItem(RequestID reqID) throws TeiidProcessingException {
RequestWorkItem result = this.requests.get(reqID);
if (result == null) {
throw new TeiidProcessingException(QueryPlugin.Event.TEIID30495, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30495, reqID));
}
return result;
}
RequestWorkItem safeGetWorkItem(Object processorID) {
return this.requests.get(processorID);
}
public WorkerPoolStatisticsMetadata getWorkerPoolStatistics() {
return this.processWorkerPool.getStats();
}
public void terminateSession(String sessionId) {
// sometimes there will not be any atomic requests pending, in that
// situation we still need to clear the master request from our map
ClientState state = this.clientState.remove(sessionId);
if (state != null) {
for (RequestID reqId : state.getRequests()) {
try {
cancelRequest(reqId);
} catch (TeiidComponentException err) {
LogManager.logWarning(LogConstants.CTX_DQP, err, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30026,reqId));
}
}
SessionMetadata session = state.session;
if (session != null) {
GlobalTableStoreImpl store = CommandContext.removeSessionScopedStore(session);
if (store != null) {
try {
store.getTempTableStore().removeTempTables();
} catch (TeiidComponentException e) {
LogManager.logDetail(LogConstants.CTX_DQP, e, "Error removing session scoped temp tables"); //$NON-NLS-1$
}
}
}
}
try {
transactionService.cancelTransactions(sessionId, false);
} catch (XATransactionException err) {
LogManager.logWarning(LogConstants.CTX_DQP, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30005,sessionId));
}
}
public boolean cancelRequest(String sessionId, long executionId) throws TeiidComponentException {
RequestID requestID = new RequestID(sessionId, executionId);
return cancelRequest(requestID);
}
public PlanNode getPlan(String sessionId, long executionId) {
RequestID requestID = new RequestID(sessionId, executionId);
if (LogManager.isMessageToBeRecorded(LogConstants.CTX_DQP, MessageLevel.DETAIL)) {
LogManager.logDetail(LogConstants.CTX_DQP, "getPlan for requestID=" + requestID); //$NON-NLS-1$
}
RequestWorkItem workItem = safeGetWorkItem(requestID);
if (workItem == null) {
return null;
}
QueryProcessor qp = workItem.getProcessor();
if (qp == null) {
return null;
}
return qp.getProcessorPlan().getDescriptionProperties();
}
private boolean cancelRequest(RequestID requestID) throws TeiidComponentException {
if (LogManager.isMessageToBeRecorded(LogConstants.CTX_DQP, MessageLevel.DETAIL)) {
LogManager.logDetail(LogConstants.CTX_DQP, "cancelQuery for requestID=" + requestID); //$NON-NLS-1$
}
boolean markCancelled = false;
RequestWorkItem workItem = safeGetWorkItem(requestID);
if (workItem != null) {
markCancelled = workItem.requestCancel();
}
if (markCancelled) {
logMMCommand(workItem, Event.CANCEL, null, null);
} else {
LogManager.logDetail(LogConstants.CTX_DQP, QueryPlugin.Util.getString("DQPCore.failed_to_cancel", requestID)); //$NON-NLS-1$
}
return markCancelled;
}
public ResultsFuture<?> closeRequest(long requestId) throws TeiidProcessingException, TeiidComponentException {
DQPWorkContext workContext = DQPWorkContext.getWorkContext();
closeRequest(workContext.getRequestID(requestId));
return ResultsFuture.NULL_FUTURE;
}
/**
* Close the request with given ID
* @param requestID
* @throws TeiidComponentException
*/
void closeRequest(RequestID requestID) throws TeiidComponentException {
if (LogManager.isMessageToBeRecorded(LogConstants.CTX_DQP, MessageLevel.DETAIL)) {
LogManager.logDetail(LogConstants.CTX_DQP, "closeQuery for requestID=" + requestID); //$NON-NLS-1$
}
RequestWorkItem workItem = safeGetWorkItem(requestID);
if (workItem != null) {
workItem.requestClose();
} else {
LogManager.logDetail(LogConstants.CTX_DQP, requestID + " close call ignored as the request has already been removed."); //$NON-NLS-1$
}
}
public Collection<TransactionMetadata> getTransactions() {
return this.transactionService.getTransactions();
}
public void terminateTransaction(String xid) throws AdminException {
this.transactionService.terminateTransaction(xid);
}
void logMMCommand(RequestWorkItem workItem, Event status, Long rowCount, Long cpuTime) {
if ((status != Event.PLAN && !LogManager.isMessageToBeRecorded(LogConstants.CTX_COMMANDLOGGING, MessageLevel.INFO))
|| (status == Event.PLAN && !LogManager.isMessageToBeRecorded(LogConstants.CTX_COMMANDLOGGING, MessageLevel.TRACE))) {
return;
}
RequestMessage msg = workItem.requestMsg;
DQPWorkContext workContext = DQPWorkContext.getWorkContext();
RequestID rID = workItem.requestID;
String txnID = null;
TransactionContext tc = workItem.getTransactionContext();
if (tc != null && tc.getTransactionType() != Scope.NONE) {
txnID = tc.getTransactionId();
}
String appName = workContext.getAppName();
// Log to request log
CommandLogMessage message = null;
if (status == Event.NEW) {
message = new CommandLogMessage(System.currentTimeMillis(), rID.toString(), txnID, workContext.getSessionId(), appName, workContext.getUserName(), workContext.getVdbName(), workContext.getVdbVersion(), msg.getCommandString(), cpuTime);
} else {
QueryProcessor qp = workItem.getProcessor();
PlanNode plan = null;
if (LogManager.isMessageToBeRecorded(LogConstants.CTX_COMMANDLOGGING, MessageLevel.TRACE) && qp != null) {
plan = qp.getProcessorPlan().getDescriptionProperties();
}
message = new CommandLogMessage(System.currentTimeMillis(), rID.toString(), txnID, workContext.getSessionId(), workContext.getUserName(), workContext.getVdbName(), workContext.getVdbVersion(), rowCount, status, plan);
}
LogManager.log(status == Event.PLAN?MessageLevel.TRACE:MessageLevel.INFO, LogConstants.CTX_COMMANDLOGGING, message);
}
public TempTableDataManager getDataTierManager() {
return this.dataTierMgr;
}
public BufferManager getBufferManager() {
return bufferManager;
}
public TransactionService getTransactionService() {
return transactionService;
}
SessionAwareCache<CachedResults> getRsCache() {
return rsCache;
}
int getProcessorTimeSlice() {
return this.config.getTimeSliceInMilli();
}
int getChunkSize() {
return chunkSize;
}
public void start(DQPConfiguration theConfig) {
this.config = theConfig;
this.authorizationValidator = config.getAuthorizationValidator();
this.chunkSize = config.getLobChunkSizeInKB() * 1024;
this.processWorkerPool = config.getTeiidExecutor();
//we don't want cancellations waiting on normal processing, so they get a small dedicated pool
//TODO: overflow to the worker pool
timeoutExecutor = ExecutorUtils.newFixedThreadPool(3, "Server Side Timeout"); //$NON-NLS-1$
this.cancellationTimer = new EnhancedTimer(timeoutExecutor, timeoutExecutor);
this.maxActivePlans = config.getMaxActivePlans();
if (this.maxActivePlans > config.getMaxThreads()) {
LogManager.logWarning(LogConstants.CTX_DQP, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30006, this.maxActivePlans, config.getMaxThreads()));
this.maxActivePlans = config.getMaxThreads();
}
//for now options are scoped to the engine - vdb scoping is a todo
options = new Options();
options.setAssumeMatchingCollation(false);
options.setProperties(config.getProperties());
PropertiesUtils.setBeanProperties(options, options.getProperties(), "org.teiid", true); //$NON-NLS-1$
this.bufferManager.setOptions(options);
//hack to set the max active plans
this.bufferManager.setMaxActivePlans(this.maxActivePlans);
try {
this.bufferManager.initialize();
} catch (TeiidComponentException e) {
throw new TeiidRuntimeException(QueryPlugin.Event.TEIID30496, e);
}
this.userRequestSourceConcurrency = config.getUserRequestSourceConcurrency();
if (this.userRequestSourceConcurrency < 1) {
this.userRequestSourceConcurrency = Math.min(config.getMaxThreads(), 2*config.getMaxThreads()/this.maxActivePlans);
}
DataTierManagerImpl processorDataManager = new DataTierManagerImpl(this, this.bufferManager, this.config.isDetectingChangeEvents());
processorDataManager.setEventDistributor(eventDistributor);
dataTierMgr = new TempTableDataManager(processorDataManager, this.bufferManager, this.rsCache);
dataTierMgr.setExecutor(new TempTableDataManager.RequestExecutor() {
@Override
public void execute(String command, List<?> parameters) {
final String sessionId = DQPWorkContext.getWorkContext().getSessionId();
RequestMessage request = new RequestMessage(command);
request.setParameterValues(parameters);
request.setStatementType(StatementType.PREPARED);
ResultsFuture<ResultsMessage> result;
try {
result = executeRequest(0, request);
} catch (TeiidProcessingException e) {
throw new TeiidRuntimeException(e);
}
result.addCompletionListener(new ResultsFuture.CompletionListener<ResultsMessage>() {
@Override
public void onCompletion(
ResultsFuture<ResultsMessage> future) {
terminateSession(sessionId);
}
});
}
@Override
public boolean isShutdown() {
return shutdown;
}
});
dataTierMgr.setEventDistributor(eventDistributor);
LogManager.logDetail(LogConstants.CTX_DQP, "DQPCore started maxThreads", this.config.getMaxThreads(), "maxActivePlans", this.maxActivePlans, "source concurrency", this.userRequestSourceConcurrency); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
public void setBufferManager(BufferManager mgr) {
this.bufferManager = mgr;
}
public void setTransactionService(TransactionService service) {
this.transactionService = service;
}
public void setEventDistributor(EventDistributor eventDistributor) {
this.eventDistributor = eventDistributor;
}
@Override
public boolean cancelRequest(long requestID)
throws TeiidProcessingException, TeiidComponentException {
DQPWorkContext workContext = DQPWorkContext.getWorkContext();
return this.cancelRequest(workContext.getRequestID(requestID));
}
// local txn
public ResultsFuture<?> begin() throws XATransactionException {
String threadId = DQPWorkContext.getWorkContext().getSessionId();
this.getTransactionService().begin(threadId);
return ResultsFuture.NULL_FUTURE;
}
// local txn
public ResultsFuture<?> commit() throws XATransactionException {
final String threadId = DQPWorkContext.getWorkContext().getSessionId();
Callable<Void> processor = new Callable<Void>() {
@Override
public Void call() throws Exception {
getTransactionService().commit(threadId);
return null;
}
};
return addWork(processor, 0);
}
// local txn
public ResultsFuture<?> rollback() throws XATransactionException {
final String threadId = DQPWorkContext.getWorkContext().getSessionId();
Callable<Void> processor = new Callable<Void>() {
@Override
public Void call() throws Exception {
getTransactionService().rollback(threadId);
return null;
}
};
return addWork(processor, 0);
}
// global txn
public ResultsFuture<?> commit(final XidImpl xid, final boolean onePhase) throws XATransactionException {
Callable<Void> processor = new Callable<Void>() {
@Override
public Void call() throws Exception {
DQPWorkContext workContext = DQPWorkContext.getWorkContext();
getTransactionService().commit(workContext.getSessionId(), xid, onePhase, workContext.getSession().isEmbedded());
return null;
}
};
return addWork(processor, 0);
}
// global txn
public ResultsFuture<?> end(XidImpl xid, int flags) throws XATransactionException {
DQPWorkContext workContext = DQPWorkContext.getWorkContext();
this.getTransactionService().end(workContext.getSessionId(), xid, flags, workContext.getSession().isEmbedded());
return ResultsFuture.NULL_FUTURE;
}
// global txn
public ResultsFuture<?> forget(XidImpl xid) throws XATransactionException {
DQPWorkContext workContext = DQPWorkContext.getWorkContext();
this.getTransactionService().forget(workContext.getSessionId(), xid, workContext.getSession().isEmbedded());
return ResultsFuture.NULL_FUTURE;
}
// global txn
public ResultsFuture<Integer> prepare(final XidImpl xid) throws XATransactionException {
Callable<Integer> processor = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
DQPWorkContext workContext = DQPWorkContext.getWorkContext();
return getTransactionService().prepare(workContext.getSessionId(),xid, workContext.getSession().isEmbedded());
}
};
return addWork(processor, 10);
}
private <T> ResultsFuture<T> addWork(final Callable<T> processor, int priority) {
final ResultsFuture<T> result = new ResultsFuture<T>();
final ResultsReceiver<T> receiver = result.getResultsReceiver();
Runnable r = new Runnable() {
@Override
public void run() {
try {
receiver.receiveResults(processor.call());
} catch (Throwable t) {
receiver.exceptionOccurred(t);
}
}
};
FutureWork<T> work = new FutureWork<T>(r, null, priority);
if (DQPWorkContext.getWorkContext().useCallingThread()) {
work.run();
} else {
this.addWork(work);
}
return result;
}
// global txn
public ResultsFuture<Xid[]> recover(int flag) throws XATransactionException {
ResultsFuture<Xid[]> result = new ResultsFuture<Xid[]>();
DQPWorkContext workContext = DQPWorkContext.getWorkContext();
result.getResultsReceiver().receiveResults(this.getTransactionService().recover(flag, workContext.getSession().isEmbedded()));
return result;
}
// global txn
public ResultsFuture<?> rollback(final XidImpl xid) throws XATransactionException {
Callable<Void> processor = new Callable<Void>() {
@Override
public Void call() throws Exception {
DQPWorkContext workContext = DQPWorkContext.getWorkContext();
getTransactionService().rollback(workContext.getSessionId(),xid, workContext.getSession().isEmbedded());
return null;
}
};
return addWork(processor, 0);
}
// global txn
public ResultsFuture<?> start(final XidImpl xid, final int flags, final int timeout)
throws XATransactionException {
final DQPWorkContext workContext = DQPWorkContext.getWorkContext();
if (workContext.getSession().isEmbedded()) {
//must be a synch call regardless since the txn should be associated with the thread.
getTransactionService().start(workContext.getSessionId(), xid, flags, timeout, true);
return ResultsFuture.NULL_FUTURE;
}
Callable<Void> processor = new Callable<Void>() {
@Override
public Void call() throws Exception {
getTransactionService().start(workContext.getSessionId(), xid, flags, timeout, false);
return null;
}
};
return addWork(processor, 100);
}
public MetadataResult getMetadata(long requestID)
throws TeiidComponentException, TeiidProcessingException {
DQPWorkContext workContext = DQPWorkContext.getWorkContext();
MetaDataProcessor processor = new MetaDataProcessor(this, this.prepPlanCache, workContext.getVdbName(), workContext.getVdbVersion());
return processor.processMessage(workContext.getRequestID(requestID), workContext, null, true);
}
public MetadataResult getMetadata(long requestID, String preparedSql,
boolean allowDoubleQuotedVariable)
throws TeiidComponentException, TeiidProcessingException {
DQPWorkContext workContext = DQPWorkContext.getWorkContext();
checkActive(workContext);
MetaDataProcessor processor = new MetaDataProcessor(this, this.prepPlanCache, workContext.getVdbName(), workContext.getVdbVersion());
return processor.processMessage(workContext.getRequestID(requestID), workContext, preparedSql, allowDoubleQuotedVariable);
}
private void checkActive(DQPWorkContext workContext)
throws TeiidProcessingException {
if (workContext.getVDB().getStatus() != Status.ACTIVE) {
throw new TeiidProcessingException(QueryPlugin.Event.TEIID31099, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID31099, workContext.getVDB(), workContext.getVDB().getStatus()));
}
}
public boolean isExceptionOnMaxSourceRows() {
return this.config.isExceptionOnMaxSourceRows();
}
public int getMaxSourceRows() {
return this.config.getMaxSourceRows();
}
public int getMaxRowsFetchSize() {
return this.config.getMaxRowsFetchSize();
}
public void setResultsetCache(SessionAwareCache<CachedResults> cache) {
this.rsCache = cache;
}
public void setPreparedPlanCache(SessionAwareCache<PreparedPlan> cache) {
this.prepPlanCache = cache;
}
public int getUserRequestSourceConcurrency() {
return userRequestSourceConcurrency;
}
void setUserRequestSourceConcurrency(int userRequestSourceConcurrency) {
this.userRequestSourceConcurrency = userRequestSourceConcurrency;
}
public int getMaxActivePlans() {
return maxActivePlans;
}
SessionAwareCache<PreparedPlan> getPrepPlanCache() {
return prepPlanCache;
}
public String getRuntimeVersion() {
return ApplicationInfo.getInstance().getBuildNumber();
}
public interface ResultsListener {
void onResults(List<String> columns, List<? extends List<?>> results) throws Exception;
}
/**
* Execute the given query asynchly. Has a hard limit of only returning max rows fetch size rows.
* @param command
* @param vdb
* @param user
* @param app
* @param timeoutInMilli
* @param engine
* @param listener
* @return
* @throws Throwable
*/
public static ResultsFuture<?> executeQuery(final Object command, final VDBMetaData vdb, final String user, final String app,
final long timeoutInMilli, final DQPCore engine, final ResultsListener listener) throws Throwable {
final SessionMetadata session = TempTableDataManager.createTemporarySession(user, app, vdb);
final long requestID = 0L;
DQPWorkContext workContext = new DQPWorkContext();
if (engine.localProfile != null) {
workContext.setConnectionProfile(engine.localProfile);
}
workContext.setUseCallingThread(true);
workContext.setSession(session);
workContext.setAdmin(true);
final ResultsFuture<Void> resultFuture = new ResultsFuture<Void>();
resultFuture.addCompletionListener(new ResultsFuture.CompletionListener<Void>() {
@SuppressWarnings("unchecked")
@Override
public void onCompletion(ResultsFuture<Void> future) {
ResultsFuture<?> response;
try {
response = engine.closeRequest(requestID);
response.addCompletionListener(new ResultsFuture.CompletionListener() {
@Override
public void onCompletion(ResultsFuture future) {
engine.terminateSession(session.getSessionId());
}
}
);
} catch (Exception e) {
engine.terminateSession(session.getSessionId());
}
}
});
workContext.runInContext(new Callable<Void>() {
@Override
public Void call() throws Exception {
RequestMessage request = new RequestMessage();
if (command instanceof String) {
request.setCommands((String)command);
} else {
request.setCommands(command.toString());
request.setCommand(command);
}
request.setExecutionId(requestID);
request.setRowLimit(engine.getMaxRowsFetchSize()); // this would limit the number of rows that are returned.
ResultsFuture<ResultsMessage> message = engine.executeRequest(requestID, request, timeoutInMilli);
message.addCompletionListener(new ResultsFuture.CompletionListener<ResultsMessage>() {
@Override
public void onCompletion(
ResultsFuture<ResultsMessage> future) {
try {
ResultsMessage rm = future.get();
if (rm.getException() != null) {
throw rm.getException();
}
if (rm.isUpdateResult()) {
listener.onResults(Arrays.asList("update-count"), rm.getResultsList()); //$NON-NLS-1$
resultFuture.getResultsReceiver().receiveResults(null);
}
else {
processResult(rm);
}
} catch (Exception e) {
resultFuture.getResultsReceiver().exceptionOccurred(e);
}
}
private void processResult(ResultsMessage rm) throws Exception {
if (rm.getException() != null) {
throw rm.getException();
}
listener.onResults(Arrays.asList(rm.getColumnNames()), rm.getResultsList());
if (rm.getFinalRow() == -1 || rm.getLastRow() < rm.getFinalRow()) {
ResultsFuture<ResultsMessage> next = engine.processCursorRequest(requestID, rm.getLastRow()+1, 1024);
next.addCompletionListener(new ResultsFuture.CompletionListener<ResultsMessage>() {
@Override
public void onCompletion(
ResultsFuture<ResultsMessage> future) {
try {
processResult(future.get());
} catch (Exception e) {
resultFuture.getResultsReceiver().exceptionOccurred(e);
}
}
});
} else {
resultFuture.getResultsReceiver().receiveResults(null);
}
}
});
return null;
}
});
return resultFuture;
}
/**
* The localprofile to use if this is an embedded DQP
* @param localProfile
*/
public void setLocalProfile(LocalProfile localProfile) {
this.localProfile = localProfile;
}
}