go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/logdog/client/coordinator/fetcher.go (about)

     1  // Copyright 2017 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package coordinator
    16  
    17  import (
    18  	"context"
    19  	"sync"
    20  	"time"
    21  
    22  	"go.chromium.org/luci/common/clock"
    23  	log "go.chromium.org/luci/common/logging"
    24  	"go.chromium.org/luci/logdog/api/logpb"
    25  	"go.chromium.org/luci/logdog/common/fetcher"
    26  	"go.chromium.org/luci/logdog/common/types"
    27  )
    28  
    29  // coordinatorSource is a fetcher.Source implementation that uses the
    30  // Coordinator API.
    31  type coordinatorSource struct {
    32  	sync.Mutex
    33  
    34  	stream *Stream
    35  	tidx   types.MessageIndex
    36  
    37  	requireCompleteStream bool
    38  
    39  	streamState *LogStream
    40  }
    41  
    42  func (s *coordinatorSource) LogEntries(c context.Context, req *fetcher.LogRequest) (
    43  	[]*logpb.LogEntry, types.MessageIndex, error) {
    44  	s.Lock()
    45  	defer s.Unlock()
    46  
    47  	params := append(make([]GetParam, 0, 4),
    48  		LimitBytes(int(req.Bytes)),
    49  		LimitCount(req.Count),
    50  		Index(req.Index),
    51  	)
    52  
    53  	// If we haven't terminated, use this opportunity to fetch/update our stream
    54  	// state.
    55  	var streamState LogStream
    56  	reqStream := (s.streamState == nil || s.streamState.State.TerminalIndex < 0)
    57  	if reqStream {
    58  		params = append(params, WithState(&streamState))
    59  	}
    60  
    61  	delayTimer := clock.NewTimer(c)
    62  	defer delayTimer.Stop()
    63  	for {
    64  		logs, err := s.stream.Get(c, params...)
    65  
    66  		// TODO(iannucci,dnj): use retry module + transient tags instead
    67  		delayTimer.Reset(5 * time.Second)
    68  		switch err {
    69  		case nil:
    70  			if reqStream {
    71  				s.streamState = &streamState
    72  				s.tidx = streamState.State.TerminalIndex
    73  			}
    74  			return logs, s.tidx, nil
    75  
    76  		case ErrNoSuchStream:
    77  			if s.requireCompleteStream {
    78  				return nil, 0, err
    79  			}
    80  
    81  			log.WithError(err).Warningf(c, "Stream does not exist. Sleeping pending registration.")
    82  
    83  			// Delay, interrupting if our Context is interrupted.
    84  			if tr := <-delayTimer.GetC(); tr.Incomplete() {
    85  				return nil, 0, tr.Err
    86  			}
    87  
    88  		default:
    89  			if err != nil {
    90  				return nil, 0, err
    91  			}
    92  		}
    93  	}
    94  }
    95  
    96  // Descriptor returns the LogStreamDescriptor for this stream, if known,
    97  // or returns nil.
    98  func (s *coordinatorSource) Descriptor() *logpb.LogStreamDescriptor {
    99  	if s.streamState != nil {
   100  		return &s.streamState.Desc
   101  	}
   102  	return nil
   103  }
   104  
   105  // Fetcher returns a Fetcher implementation for this Stream.
   106  //
   107  // If you pass a nil fetcher.Options, a default option set will be used. The
   108  // o.Source field will always be overwritten to be based off this stream.
   109  func (s *Stream) Fetcher(c context.Context, o *fetcher.Options) *fetcher.Fetcher {
   110  	if o == nil {
   111  		o = &fetcher.Options{}
   112  	} else {
   113  		o = &(*o)
   114  	}
   115  	o.Source = &coordinatorSource{
   116  		stream: s, tidx: -1, requireCompleteStream: o.RequireCompleteStream}
   117  	return fetcher.New(c, *o)
   118  }