InvocationResponse ClientImpl::invoke(Procedure &proc) throw (voltdb::Exception, voltdb::NoConnectionsException, voltdb::UninitializedParamsException, voltdb::LibEventException) { if (m_bevs.empty()) { throw voltdb::NoConnectionsException(); } int32_t messageSize = proc.getSerializedSize(); ScopedByteBuffer sbb(messageSize); int64_t clientData = m_nextRequestId++; proc.serializeTo(&sbb, clientData); struct bufferevent *bev = m_bevs[m_nextConnectionIndex++ % m_bevs.size()]; InvocationResponse response; boost::shared_ptr<ProcedureCallback> callback(new SyncCallback(&response)); struct evbuffer *evbuf = bufferevent_get_output(bev); if (evbuffer_add(evbuf, sbb.bytes(), static_cast<size_t>(sbb.remaining()))) { throw voltdb::LibEventException(); } m_outstandingRequests++; (*m_callbacks[bev])[clientData] = callback; if (event_base_dispatch(m_base) == -1) { throw voltdb::LibEventException(); } m_loopBreakRequested = false; return response; }
void ClientImpl::invoke(Procedure &proc, boost::shared_ptr<ProcedureCallback> callback) throw (voltdb::Exception, voltdb::NoConnectionsException, voltdb::UninitializedParamsException, voltdb::LibEventException, voltdb::ElasticModeMismatchException) { if (callback.get() == NULL) { throw voltdb::NullPointerException(); } if (m_bevs.empty()) { throw voltdb::NoConnectionsException(); } if (m_outstandingRequests >= m_maxOutstandingRequests) { if (m_listener.get() != NULL) { try { m_listener->backpressure(true); } catch (const std::exception& e) { std::cerr << "Exception thrown on invocation of backpressure callback: " << e.what() << std::endl; } } // We are overloaded, we need to reject traffic and notify the caller callback->abandon(ProcedureCallback::TOO_BUSY); return; } //do not call the procedures if hashinator is in the LEGACY mode if (!m_distributer.isUpdating() && !m_distributer.isElastic()) { //todo: need to remove the connection throw voltdb::ElasticModeMismatchException(); } int32_t messageSize = proc.getSerializedSize(); ScopedByteBuffer sbb(messageSize); int64_t clientData = m_nextRequestId++; proc.serializeTo(&sbb, clientData); /* * Decide what connection to buffer the event on. * First each connection is checked for backpressure. If there is a connection * with no backpressure break. * * If none can be found, notify the client application and let it decide whether to queue anyways * or run the event loop until there is no more backpressure. * * If queuing anyways just pick the next connection. * * If waiting for no more backpressure, then set the m_invocationBlockedOnBackpressure flag * so that the write callback knows to break the event loop once there is a connection with no backpressure and * then enter the event loop. * It is possible for the event loop to break early while there is still backpressure because an unrelated callback * may have been invoked and that unrelated callback may have requested that the event loop be broken. In that * case just queue the request to the next connection despite backpressure so that loop break occurs as requested. * Also set the m_invocationBlockedOnBackpressure flag back to false so that the write callback won't spuriously * break the event loop later. */ struct bufferevent *bev = NULL; while (true) { if (m_ignoreBackpressure) { bev = m_bevs[++m_nextConnectionIndex % m_bevs.size()]; break; } //Assume backpressure if the number of outstanding requests is too large, i.e. leave bev == NULL if (m_outstandingRequests <= m_maxOutstandingRequests) { for (size_t ii = 0; ii < m_bevs.size(); ii++) { bev = m_bevs[++m_nextConnectionIndex % m_bevs.size()]; if (m_backpressuredBevs.find(bev) != m_backpressuredBevs.end()) { bev = NULL; } else { break; } } } if (bev) { break; } else { bool callEventLoop = true; if (m_listener.get() != NULL) { try { m_ignoreBackpressure = true; callEventLoop = !m_listener->backpressure(true); m_ignoreBackpressure = false; } catch (const std::exception& e) { std::cerr << "Exception thrown on invocation of backpressure callback: " << e.what() << std::endl; } } if (callEventLoop) { m_invocationBlockedOnBackpressure = true; if (event_base_dispatch(m_base) == -1) { throw voltdb::LibEventException(); } if (m_loopBreakRequested) { m_loopBreakRequested = false; m_invocationBlockedOnBackpressure = false; bev = m_bevs[++m_nextConnectionIndex % m_bevs.size()]; } } else { bev = m_bevs[++m_nextConnectionIndex % m_bevs.size()]; break; } } } //route transaction to correct event if client affinity is enabled and hashinator updating is not in progress //elastic scalability is disabled if (m_useClientAffinity && !m_distributer.isUpdating()) { struct bufferevent *routed_bev = routeProcedure(proc, sbb); // Check if the routed_bev is valid and has not been removed due to lost connection if ((routed_bev) && (m_callbacks.find(routed_bev) != m_callbacks.end())) bev = routed_bev; } struct evbuffer *evbuf = bufferevent_get_output(bev); if (evbuffer_add(evbuf, sbb.bytes(), static_cast<size_t>(sbb.remaining()))) { throw voltdb::LibEventException(); } m_outstandingRequests++; (*m_callbacks[bev])[clientData] = callback; if (evbuffer_get_length(evbuf) > 262144) { m_backpressuredBevs.insert(bev); } return; }