/// <summary> /// Completes or retries the buffer flush operation. /// </summary> private void FlushBufferCompleteOrRetry( DataStreamerClientBuffer <TK, TV> buffer, ClientSocket socket, TaskCompletionSource <object> tcs, Exception exception) { if (exception == null) { // Successful flush. Interlocked.Add(ref _entriesSent, buffer.Count); ReturnPooledArray(buffer.Entries); tcs.SetResult(null); return; } if (_cancelled || (!socket.IsDisposed && !ShouldRetry(exception))) { // Socket is still connected: this error does not need to be retried. ReturnPooledArray(buffer.Entries); tcs.SetException(exception); return; } // Release receiver thread, perform retry on a separate thread. ThreadPool.QueueUserWorkItem(_ => FlushBufferRetry(buffer, socket, tcs)); }
/// <summary> /// Initializes a new instance of <see cref="DataStreamerClientPerNodeBuffer{TK,TV}"/>. /// </summary> /// <param name="client">Streamer.</param> /// <param name="socket">Socket for the node.</param> public DataStreamerClientPerNodeBuffer(DataStreamerClient <TK, TV> client, ClientSocket socket) { Debug.Assert(client != null); _client = client; _socket = socket; _semaphore = new SemaphoreSlim(client.Options.PerNodeParallelOperations); _buffer = new DataStreamerClientBuffer <TK, TV>(_client.GetPooledArray(), this, null); }
/// <summary> /// Initializes a new instance of <see cref="DataStreamerClientBuffer{TK,TV}"/>. /// </summary> public DataStreamerClientBuffer( DataStreamerClientEntry <TK, TV>[] entries, DataStreamerClientPerNodeBuffer <TK, TV> parent, DataStreamerClientBuffer <TK, TV> previous) { Debug.Assert(parent != null); _entries = entries; _parent = parent; _previous = previous; }
/// <summary> /// Writes buffer data to the specified writer. /// </summary> private void WriteBuffer(DataStreamerClientBuffer <TK, TV> buffer, BinaryWriter w) { w.WriteInt(_cacheId); w.WriteByte((byte)_flags); w.WriteInt(ServerBufferSizeAuto); // Server per-node buffer size. w.WriteInt(ServerBufferSizeAuto); // Server per-thread buffer size. if (_options.Receiver != null) { var rcvHolder = new StreamReceiverHolder(_options.Receiver, (rec, grid, cache, stream, keepBinary) => StreamReceiverHolder.InvokeReceiver((IStreamReceiver <TK, TV>)rec, grid, cache, stream, keepBinary)); w.WriteObjectDetached(rcvHolder); w.WriteByte(ClientPlatformId.Dotnet); } else { w.WriteObject <object>(null); } var count = buffer.Count; w.WriteInt(count); var entries = buffer.Entries; for (var i = 0; i < count; i++) { var entry = entries[i]; if (entry.IsEmpty) { continue; } w.WriteObjectDetached(entry.Key); if (entry.Remove) { w.WriteObject <object>(null); } else { w.WriteObjectDetached(entry.Val); } } }
/// <summary> /// Re-adds obsolete buffer entries to new buffers and returns the array to the pool. /// </summary> private void ReAddEntriesAndReturnBuffer(DataStreamerClientBuffer <TK, TV> buffer) { var count = buffer.Count; var entries = buffer.Entries; for (var i = 0; i < count; i++) { var entry = entries[i]; if (!entry.IsEmpty) { AddNoLock(entry); } } ReturnPooledArray(entries); }
/// <summary> /// Checks if entire buffer chain is already flushed. /// </summary> private bool CheckChainFlushed() { var previous = _previous; if (previous == null) { return(_flushed); } if (!previous.CheckChainFlushed()) { return(false); } _previous = null; return(_flushed); }
/// <summary> /// Flushes the specified buffer asynchronously. /// </summary> internal Task FlushBufferAsync( DataStreamerClientBuffer <TK, TV> buffer, ClientSocket socket, SemaphoreSlim semaphore) { semaphore.Wait(); var tcs = new TaskCompletionSource <object>(); FlushBufferInternalAsync(buffer, socket, tcs); return(tcs.Task.ContWith(t => { semaphore.Release(); _exception = _exception ?? t.Exception; return t.Result; }, TaskContinuationOptions.ExecuteSynchronously)); }
private void FlushBufferInternalAsync( DataStreamerClientBuffer <TK, TV> buffer, ClientSocket socket, TaskCompletionSource <object> tcs) { try { socket.DoOutInOpAsync( ClientOp.DataStreamerStart, ctx => WriteBuffer(buffer, ctx.Writer), ctx => (object)null, syncCallback: true) .ContWith( t => FlushBufferCompleteOrRetry(buffer, socket, tcs, t.Exception), TaskContinuationOptions.ExecuteSynchronously); } catch (Exception exception) { FlushBufferCompleteOrRetry(buffer, socket, tcs, exception); } }
private void FlushBufferRetry( DataStreamerClientBuffer <TK, TV> buffer, ClientSocket failedSocket, TaskCompletionSource <object> tcs) { try { // Connection failed. Remove disconnected socket from the map. DataStreamerClientPerNodeBuffer <TK, TV> removed; _buffers.TryRemove(failedSocket, out removed); // Re-add entries to other buffers. ReAddEntriesAndReturnBuffer(buffer); if (removed != null) { var remaining = removed.Close(); while (remaining != null) { if (remaining.MarkFlushed()) { ReAddEntriesAndReturnBuffer(remaining); } remaining = remaining.Previous; } } // Note: if initial flush was caused by full buffer, not requested by the user, // we don't need to force flush everything here - just re-add entries to other buffers. FlushInternalAsync().ContWith(flushTask => flushTask.SetAsResult(tcs)); } catch (Exception e) { tcs.SetException(e); } }
/// <summary> /// Flushes the specified buffer asynchronously. /// </summary> public Task FlushBufferAsync(DataStreamerClientBuffer <TK, TV> buffer) { return(_client.FlushBufferAsync(buffer, _socket, _semaphore)); }