/// <summary> /// Try to write the item provided. Thread safe. /// </summary> private void SendQueueProcess(OperationState state) { if (!_canWriteNext) { //Double-checked locking for best performance _writeQueue.Enqueue(state); return; } short streamId = -1; lock (_writeQueueLock) { if (!_canWriteNext) { //We have to recheck as the world can change since the last instruction _writeQueue.Enqueue(state); return; } //Check if Cassandra can process a new operation if (!_freeOperations.TryPop(out streamId)) { //Queue it up for later. //When receiving the next complete message, we can process it. _writeQueue.Enqueue(state); _logger.Info("Enqueued: " + _writeQueue.Count + ", if this message is recurrent consider configuring more connections per host or lower the pressure"); return; } //Prevent the next to process _canWriteNext = false; } //At this point: //We have a valid stream id //Only 1 thread at a time can be here. try { _logger.Verbose("Sending #" + streamId + " for " + state.Request.GetType().Name); var frameStream = state.Request.GetFrame(streamId).Stream; _pendingOperations.AddOrUpdate(streamId, state, (k, oldValue) => state); //We will not use the request, stop reference it. state.Request = null; //Start sending it _tcpSocket.Write(frameStream); } catch (Exception ex) { //Prevent dead locking _canWriteNext = true; _logger.Error(ex); //The request was not written _pendingOperations.TryRemove(streamId, out state); _freeOperations.Push(streamId); throw; } }
private void SendQueueProcessItem(OperationState state) { short streamId; //Check if Cassandra can process a new operation if (!_freeOperations.TryPop(out streamId)) { //Queue it up for later. //When receiving the next complete message, we can process it. _writeQueue.Enqueue(state); _logger.Info("Enqueued: {0}, if this message is recurrent consider configuring more connections per host or lower the pressure", _writeQueue.Count); Interlocked.Exchange(ref _canWriteNext, 1); return; } //We have a valid stream id //Only 1 thread at a time can be here. _logger.Verbose("Sending #" + streamId + " for " + state.Request.GetType().Name); _pendingOperations.AddOrUpdate(streamId, state, (k, oldValue) => state); try { var frameStream = state.Request.GetFrame(streamId).Stream; //We will not use the request any more, stop reference it. state.Request = null; //Start sending it _tcpSocket.Write(frameStream); //Closure state variable var delegateState = state; if (Configuration.SocketOptions.ReadTimeoutMillis > 0 && Configuration.Timer != null) { state.Timeout = Configuration.Timer.NewTimeout(() => OnTimeout(delegateState), Configuration.SocketOptions.ReadTimeoutMillis); } } catch (Exception ex) { //There was an error while serializing or begin sending _logger.Error(ex); //The request was not written, clear it from pending operations RemoveFromPending(streamId); //Callback with the Exception state.InvokeCallback(ex); } }
private void RunWriteQueueAction() { //Dequeue all items until threshold is passed long totalLength = 0; RecyclableMemoryStream stream = null; while (totalLength < CoalescingThreshold) { OperationState state; if (!_writeQueue.TryDequeue(out state)) { //No more items in the write queue break; } short streamId; if (!_freeOperations.TryPop(out streamId)) { //Queue it up for later. _writeQueue.Enqueue(state); //When receiving the next complete message, we can process it. Logger.Info("Enqueued, no streamIds available. If this message is recurrent consider configuring more connections per host or lower the pressure"); break; } Logger.Verbose("Sending #{0} for {1} to {2}", streamId, state.Request.GetType().Name, Address); if (_isCanceled) { state.InvokeCallback(new SocketException((int)SocketError.NotConnected)); break; } _pendingOperations.AddOrUpdate(streamId, state, (k, oldValue) => state); Interlocked.Increment(ref _inFlight); int frameLength; try { //lazy initialize the stream stream = stream ?? (RecyclableMemoryStream)Configuration.BufferPool.GetStream(StreamWriteTag); frameLength = state.Request.WriteFrame(streamId, stream, _serializer); if (state.TimeoutMillis > 0) { var requestTimeout = Configuration.Timer.NewTimeout(OnTimeout, streamId, state.TimeoutMillis); state.SetTimeout(requestTimeout); } } catch (Exception ex) { //There was an error while serializing or begin sending Logger.Error(ex); //The request was not written, clear it from pending operations RemoveFromPending(streamId); //Callback with the Exception state.InvokeCallback(ex); break; } //We will not use the request any more, stop reference it. state.Request = null; totalLength += frameLength; } if (totalLength == 0L) { // Nothing to write, set the queue as not running Interlocked.CompareExchange(ref _writeState, WriteStateInit, WriteStateRunning); // Until now, we were preventing other threads to running the queue. // Check if we can now write: // a read could have finished (freeing streamIds) or new request could have been added to the queue if (!_freeOperations.IsEmpty && !_writeQueue.IsEmpty) { //The write queue is not empty //An item was added to the queue but we were running: try to launch a new queue RunWriteQueue(); } if (stream != null) { //The stream instance could be created if there was an exception while generating the frame stream.Dispose(); } return; } //Write and close the stream when flushed // ReSharper disable once PossibleNullReferenceException : if totalLength > 0 the stream is initialized _tcpSocket.Write(stream, () => stream.Dispose()); }