internal async Task<FetchResponse> Fetch(FetchRequest req, Connection conn) { _log.Debug("Sending FetchRequest to broker {1}. Request: {0}", req, conn); if (_etw.IsEnabled()) _etw.ProtocolFetchRequest(req.ToString()); // Detect disconnected server. Wait no less than 5sec. // If wait time exceed wait time + 3sec, consider it a timeout too //var timeout = Math.Max(5000, req.MaxWaitTime + 3000); //var cancel = new CancellationTokenSource(timeout); var tcp = await conn.GetClientAsync(); var response = await conn.Correlation.SendAndCorrelateAsync( id => Serializer.Serialize(req, id), Serializer.DeserializeFetchResponse, tcp, /*cancel.Token*/ CancellationToken.None); if (_etw.IsEnabled()) _etw.ProtocolFetchResponse(response.ToString()); return response; }
private IObservable<FetchResponse> FetchLoop() { return Observable.Create<FetchResponse>(async observer => { while (!_cancel.IsCancellationRequested) { var fetchRequest = new FetchRequest { MaxWaitTime = _consumerConfig.MaxWaitTimeMs, MinBytes = _consumerConfig.MinBytesPerFetch, Topics = _topicPartitions. Where(tp => { var enabled = tp.FlowControlEnabled; if(!enabled) _log.Debug("#{0} Ignoring partition because flow controll is off {1}", _id, tp); return enabled; }). GroupBy(tp=>tp.Topic). Select(t => new FetchRequest.TopicData { Topic = t.Key, Partitions = t. Select(p => new FetchRequest.PartitionData { Partition = p.PartitionId, FetchOffset = p.CurrentOffset, MaxBytes = _consumerConfig.MaxBytesPerFetch }).ToArray() }).ToArray() }; if (fetchRequest.Topics.Length == 0) { _log.Debug("#{0} No partitions subscribed to fetcher. Waiting for _wakeupSignal signal", _id); EtwTrace.Log.FetcherSleep(_id); await _wakeupSignal.FirstAsync(); EtwTrace.Log.FetcherWakeup(_id); if(_cancel.IsCancellationRequested) { _log.Debug("#{0}Cancel detected. Quitting FetchLoop", _id); break; } _log.Debug("#{0} Received _wakeupSignal. Have {1} partitions subscribed", _id, _topicPartitions.Count); continue; } // issue fetch FetchResponse fetch; try { EtwTrace.Log.FetcherFetchRequest(_id, fetchRequest.Topics.Length, fetchRequest.Topics.Sum(td => td.Partitions.Length), _broker.Host, _broker.Port, _broker.NodeId); fetch = await _protocol.Fetch(fetchRequest, _broker.Conn); EtwTrace.Log.FetcherFetchResponse(_id); // if any TopicPartitions have an error, fail them with the Cluster. fetch.Topics.SelectMany(t => t.Partitions.Select(p => new PartitionStateChangeEvent(t.Topic, p.Partition, p.ErrorCode))) .Where(ps => !ps.ErrorCode.IsSuccess()) .ForEach(ps => _cluster.NotifyPartitionStateChange(ps)); if (_log.IsDebugEnabled && fetch.Topics.Any(t=>t.Partitions.Any(p=>p.Messages.Length > 0))) _log.Debug("#{0}: got FetchResponse from {2} with messages: {1}", _id, _broker.Conn, fetch.ToString(true)); } //catch (TaskCanceledException) //{ // // Usually reason of fetch to time out is broker closing Tcp socket. // // Due to Tcp specifics, there are situations when closed connection can not be detected, // // thus we need to implement timeout to detect it and restart connection. // _log.Info("#{0} Fetch timed out {1}", _id, this); // // Continue so that socket exception happen and handle exception // // in uniform way // continue; //} catch (ObjectDisposedException e) { if (!_cancel.IsCancellationRequested) { _log.Debug("#{0} connection closed", _id); observer.OnError(e); return; } break; } catch (CorrelationLoopException e) { if (!_cancel.IsCancellationRequested) { _log.Debug("#{0} connection closed", _id); observer.OnError(e); return; } break; } catch (SocketException e) { if (!_cancel.IsCancellationRequested) { _log.Info(e, "#{0} Connection failed. {1}", _id, e.Message); observer.OnError(e); return; } break; } catch (Exception e) { if (!_cancel.IsCancellationRequested) { _log.Error(e, "#{0} Fetcher failed", _id); observer.OnError(e); return; } break; } // if timeout, we got empty response if (fetch.Topics.Any(t => t.Partitions.Any(p => p.Messages.Length > 0))) { observer.OnNext(fetch); } } _log.Info("Cancellation Requested. Shutting Down."); observer.OnCompleted(); }); }