// sendReadyChunksToClient reads from the readyChunks channel for a particular // client, sending those chunks to the remote system. This function is also // responsible for snapshotting progress and unlocking the readers after it has // successfully sent. func (s *Supervisor) sendReadyChunksToClient(client client.Client) { backoff := &ExponentialBackoff{Minimum: 50 * time.Millisecond, Maximum: 5000 * time.Millisecond} for { var readyChunk *readyChunk select { case <-s.stopRequest: return case readyChunk = <-s.retryChunks: // got a retry chunk; use it default: // pull from the default readyChunk queue select { case <-s.stopRequest: return case readyChunk = <-s.readyChunks: // got a chunk } } if readyChunk != nil { GlobalStatistics.SetClientStatus(client.Name(), clientStatusSending) if err := s.sendChunk(client, readyChunk.Chunk); err != nil { grohl.Report(err, grohl.Data{"msg": "failed to send chunk", "resolution": "retrying"}) GlobalStatistics.SetClientStatus(client.Name(), clientStatusRetrying) // Put the chunk back on the queue for someone else to try select { case <-s.stopRequest: return case s.retryChunks <- readyChunk: // continue } // Backoff select { case <-s.stopRequest: return case <-time.After(backoff.Next()): // continue } } else { backoff.Reset() GlobalStatistics.IncrementClientLinesSent(client.Name(), len(readyChunk.Chunk)) // Snapshot progress if err := s.acknowledgeChunk(readyChunk.Chunk); err != nil { grohl.Report(err, grohl.Data{"msg": "failed to acknowledge progress", "resolution": "skipping"}) } s.readerPool.UnlockAll(readyChunk.LockedReaders) } } } }