|
|
|
@ -17,13 +17,21 @@ import ( |
|
|
|
|
util_log "github.com/grafana/loki/pkg/util/log" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
const bufferSizeForTailResponse = 5 |
|
|
|
|
const ( |
|
|
|
|
bufferSizeForTailResponse = 5 |
|
|
|
|
bufferSizeForTailStream = 100 |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
type TailServer interface { |
|
|
|
|
Send(*logproto.TailResponse) error |
|
|
|
|
Context() context.Context |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type tailRequest struct { |
|
|
|
|
stream logproto.Stream |
|
|
|
|
lbs labels.Labels |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type tailer struct { |
|
|
|
|
id uint32 |
|
|
|
|
orgID string |
|
|
|
@ -31,6 +39,7 @@ type tailer struct { |
|
|
|
|
pipeline syntax.Pipeline |
|
|
|
|
pipelineMtx sync.Mutex |
|
|
|
|
|
|
|
|
|
queue chan tailRequest |
|
|
|
|
sendChan chan *logproto.Stream |
|
|
|
|
|
|
|
|
|
// Signaling channel used to notify once the tailer gets closed
|
|
|
|
@ -59,6 +68,7 @@ func newTailer(orgID string, expr syntax.LogSelectorExpr, conn TailServer, maxDr |
|
|
|
|
orgID: orgID, |
|
|
|
|
matchers: matchers, |
|
|
|
|
sendChan: make(chan *logproto.Stream, bufferSizeForTailResponse), |
|
|
|
|
queue: make(chan tailRequest, bufferSizeForTailStream), |
|
|
|
|
conn: conn, |
|
|
|
|
droppedStreams: make([]*logproto.DroppedStream, 0, maxDroppedStreams), |
|
|
|
|
maxDroppedStreams: maxDroppedStreams, |
|
|
|
@ -73,6 +83,9 @@ func (t *tailer) loop() { |
|
|
|
|
var err error |
|
|
|
|
var ok bool |
|
|
|
|
|
|
|
|
|
// Launch a go routine to receive streams sent with t.send
|
|
|
|
|
go t.receiveStreamsLoop() |
|
|
|
|
|
|
|
|
|
for { |
|
|
|
|
select { |
|
|
|
|
case <-t.conn.Context().Done(): |
|
|
|
@ -102,6 +115,37 @@ func (t *tailer) loop() { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (t *tailer) receiveStreamsLoop() { |
|
|
|
|
defer t.close() |
|
|
|
|
for { |
|
|
|
|
select { |
|
|
|
|
case <-t.conn.Context().Done(): |
|
|
|
|
return |
|
|
|
|
case <-t.closeChan: |
|
|
|
|
return |
|
|
|
|
case req, ok := <-t.queue: |
|
|
|
|
if !ok { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
streams := t.processStream(req.stream, req.lbs) |
|
|
|
|
if len(streams) == 0 { |
|
|
|
|
continue |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for _, s := range streams { |
|
|
|
|
select { |
|
|
|
|
case t.sendChan <- s: |
|
|
|
|
default: |
|
|
|
|
t.dropStream(*s) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// send sends a stream to the tailer for processing and sending to the client.
|
|
|
|
|
// It will drop the stream if the tailer is blocked or the queue is full.
|
|
|
|
|
func (t *tailer) send(stream logproto.Stream, lbs labels.Labels) { |
|
|
|
|
if t.isClosed() { |
|
|
|
|
return |
|
|
|
@ -117,16 +161,16 @@ func (t *tailer) send(stream logproto.Stream, lbs labels.Labels) { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
streams := t.processStream(stream, lbs) |
|
|
|
|
if len(streams) == 0 { |
|
|
|
|
return |
|
|
|
|
// Send stream to queue for processing asynchronously
|
|
|
|
|
// If the queue is full, drop the stream
|
|
|
|
|
req := tailRequest{ |
|
|
|
|
stream: stream, |
|
|
|
|
lbs: lbs, |
|
|
|
|
} |
|
|
|
|
for _, s := range streams { |
|
|
|
|
select { |
|
|
|
|
case t.sendChan <- s: |
|
|
|
|
default: |
|
|
|
|
t.dropStream(*s) |
|
|
|
|
} |
|
|
|
|
select { |
|
|
|
|
case t.queue <- req: |
|
|
|
|
default: |
|
|
|
|
t.dropStream(stream) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|