2019-07-22 14:34:12 +01:00
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
2021-03-23 12:14:38 +00:00
package metaloop
2019-07-22 14:34:12 +01:00
import (
"context"
2021-02-22 16:24:06 +00:00
"errors"
2020-02-25 14:16:44 +00:00
"fmt"
2019-07-22 14:34:12 +01:00
"time"
2020-02-25 14:16:44 +00:00
"github.com/spacemonkeygo/monkit/v3"
2019-07-22 14:34:12 +01:00
"github.com/zeebo/errs"
2019-12-19 18:33:59 +00:00
"golang.org/x/time/rate"
2020-12-10 15:09:44 +00:00
"storj.io/common/uuid"
2021-04-21 13:42:57 +01:00
"storj.io/storj/satellite/metabase"
2019-07-22 14:34:12 +01:00
)
2021-02-23 16:41:46 +00:00
const batchsizeLimit = 2500
2019-07-22 14:34:12 +01:00
var (
2021-03-23 12:14:38 +00:00
mon = monkit . Package ( )
// Error is a standard error class for this component.
2021-04-28 09:06:17 +01:00
Error = errs . Class ( "metainfo loop" )
2021-03-23 12:14:38 +00:00
// ErrClosed is a loop closed error.
ErrClosed = Error . New ( "loop closed" )
2019-07-22 14:34:12 +01:00
)
2020-10-27 06:59:14 +00:00
// Object is the object info passed to Observer by metainfo loop.
2021-03-01 17:01:49 +00:00
type Object metabase . LoopObjectEntry
2020-10-27 06:59:14 +00:00
2021-03-01 17:01:49 +00:00
// Expired checks if object expired relative to now.
2020-10-27 06:59:14 +00:00
func ( object * Object ) Expired ( now time . Time ) bool {
2021-03-01 17:01:49 +00:00
return object . ExpiresAt != nil && object . ExpiresAt . Before ( now )
2020-10-27 06:59:14 +00:00
}
// Segment is the segment info passed to Observer by metainfo loop.
type Segment struct {
2020-12-11 14:12:42 +00:00
Location metabase . SegmentLocation // tally, repair, graceful exit, audit
2020-12-14 12:54:22 +00:00
ExpirationDate time . Time // tally, repair
2021-03-02 12:58:23 +00:00
metabase . LoopSegmentEntry
2020-10-27 06:59:14 +00:00
}
// Expired checks if segment is expired relative to now.
func ( segment * Segment ) Expired ( now time . Time ) bool {
2020-12-14 12:54:22 +00:00
return ! segment . ExpirationDate . IsZero ( ) && segment . ExpirationDate . Before ( now )
2020-10-27 06:59:14 +00:00
}
2019-07-22 14:34:12 +01:00
// Observer is an interface defining an observer that can subscribe to the metainfo loop.
2019-09-10 14:24:16 +01:00
//
// architecture: Observer
2019-07-22 14:34:12 +01:00
type Observer interface {
2020-10-27 06:59:14 +00:00
Object ( context . Context , * Object ) error
RemoteSegment ( context . Context , * Segment ) error
InlineSegment ( context . Context , * Segment ) error
2021-04-01 11:56:39 +01:00
LoopStarted ( context . Context , LoopInfo ) error
}
// LoopInfo contains information about the current loop.
type LoopInfo struct {
Started time . Time
2019-09-12 11:38:49 +01:00
}
2020-02-13 11:01:39 +00:00
// NullObserver is an observer that does nothing. This is useful for joining
2020-07-16 15:18:02 +01:00
// and ensuring the metainfo loop runs once before you use a real observer.
2020-02-13 11:01:39 +00:00
type NullObserver struct { }
2020-07-16 15:18:02 +01:00
// Object implements the Observer interface.
2020-10-27 06:59:14 +00:00
func ( NullObserver ) Object ( context . Context , * Object ) error {
2020-02-13 11:01:39 +00:00
return nil
}
2020-07-16 15:18:02 +01:00
// RemoteSegment implements the Observer interface.
2020-10-27 06:59:14 +00:00
func ( NullObserver ) RemoteSegment ( context . Context , * Segment ) error {
2020-02-13 11:01:39 +00:00
return nil
}
2020-07-16 15:18:02 +01:00
// InlineSegment implements the Observer interface.
2020-10-27 06:59:14 +00:00
func ( NullObserver ) InlineSegment ( context . Context , * Segment ) error {
2020-02-13 11:01:39 +00:00
return nil
}
2021-04-01 11:56:39 +01:00
// LoopStarted is called at each loop start.
func ( NullObserver ) LoopStarted ( context . Context , LoopInfo ) error {
return nil
}
2019-07-22 14:34:12 +01:00
type observerContext struct {
2021-04-10 10:31:48 +01:00
trigger bool
2020-02-25 14:16:44 +00:00
observer Observer
2019-07-22 14:34:12 +01:00
ctx context . Context
done chan error
2020-02-25 14:16:44 +00:00
object * monkit . DurationDist
remote * monkit . DurationDist
inline * monkit . DurationDist
}
func newObserverContext ( ctx context . Context , obs Observer ) * observerContext {
name := fmt . Sprintf ( "%T" , obs )
key := monkit . NewSeriesKey ( "observer" ) . WithTag ( "name" , name )
return & observerContext {
observer : obs ,
ctx : ctx ,
done : make ( chan error ) ,
object : monkit . NewDurationDist ( key . WithTag ( "pointer_type" , "object" ) ) ,
inline : monkit . NewDurationDist ( key . WithTag ( "pointer_type" , "inline" ) ) ,
remote : monkit . NewDurationDist ( key . WithTag ( "pointer_type" , "remote" ) ) ,
}
}
2020-10-27 06:59:14 +00:00
func ( observer * observerContext ) Object ( ctx context . Context , object * Object ) error {
2020-02-25 14:16:44 +00:00
start := time . Now ( )
defer func ( ) { observer . object . Insert ( time . Since ( start ) ) } ( )
2020-10-27 06:59:14 +00:00
return observer . observer . Object ( ctx , object )
2020-02-25 14:16:44 +00:00
}
2020-10-27 06:59:14 +00:00
func ( observer * observerContext ) RemoteSegment ( ctx context . Context , segment * Segment ) error {
2020-02-25 14:16:44 +00:00
start := time . Now ( )
defer func ( ) { observer . remote . Insert ( time . Since ( start ) ) } ( )
2020-10-27 06:59:14 +00:00
return observer . observer . RemoteSegment ( ctx , segment )
2020-02-25 14:16:44 +00:00
}
2020-10-27 06:59:14 +00:00
func ( observer * observerContext ) InlineSegment ( ctx context . Context , segment * Segment ) error {
2020-02-25 14:16:44 +00:00
start := time . Now ( )
defer func ( ) { observer . inline . Insert ( time . Since ( start ) ) } ( )
2020-10-27 06:59:14 +00:00
return observer . observer . InlineSegment ( ctx , segment )
2019-07-22 14:34:12 +01:00
}
func ( observer * observerContext ) HandleError ( err error ) bool {
if err != nil {
observer . done <- err
observer . Finish ( )
return true
}
return false
}
func ( observer * observerContext ) Finish ( ) {
close ( observer . done )
2020-02-25 14:16:44 +00:00
name := fmt . Sprintf ( "%T" , observer . observer )
stats := allObserverStatsCollectors . GetStats ( name )
stats . Observe ( observer )
2019-07-22 14:34:12 +01:00
}
func ( observer * observerContext ) Wait ( ) error {
return <- observer . done
}
2021-03-23 12:14:38 +00:00
// Config contains configurable values for the metainfo loop.
type Config struct {
testplanet/satellite: reduce the number of places default values need to be configured
Satellites set their configuration values to default values using
cfgstruct, however, it turns out our tests don't test these values
at all! Instead, they have a completely separate definition system
that is easy to forget about.
As is to be expected, these values have drifted, and it appears
in a few cases test planet is testing unreasonable values that we
won't see in production, or perhaps worse, features enabled in
production were missed and weren't enabled in testplanet.
This change makes it so all values are configured the same,
systematic way, so it's easy to see when test values are different
than dev values or release values, and it's less hard to forget
to enable features in testplanet.
In terms of reviewing, this change should be actually fairly
easy to review, considering private/testplanet/satellite.go keeps
the current config system and the new one and confirms that they
result in identical configurations, so you can be certain that
nothing was missed and the config is all correct.
You can also check the config lock to see what actual config
values changed.
Change-Id: I6715d0794887f577e21742afcf56fd2b9d12170e
2021-05-31 22:15:00 +01:00
CoalesceDuration time . Duration ` help:"how long to wait for new observers before starting iteration" releaseDefault:"5s" devDefault:"5s" testDefault:"1s" `
2020-05-05 07:51:24 +01:00
RateLimit float64 ` help:"rate limit (default is 0 which is unlimited segments per second)" default:"0" `
testplanet/satellite: reduce the number of places default values need to be configured
Satellites set their configuration values to default values using
cfgstruct, however, it turns out our tests don't test these values
at all! Instead, they have a completely separate definition system
that is easy to forget about.
As is to be expected, these values have drifted, and it appears
in a few cases test planet is testing unreasonable values that we
won't see in production, or perhaps worse, features enabled in
production were missed and weren't enabled in testplanet.
This change makes it so all values are configured the same,
systematic way, so it's easy to see when test values are different
than dev values or release values, and it's less hard to forget
to enable features in testplanet.
In terms of reviewing, this change should be actually fairly
easy to review, considering private/testplanet/satellite.go keeps
the current config system and the new one and confirms that they
result in identical configurations, so you can be certain that
nothing was missed and the config is all correct.
You can also check the config lock to see what actual config
values changed.
Change-Id: I6715d0794887f577e21742afcf56fd2b9d12170e
2021-05-31 22:15:00 +01:00
ListLimit int ` help:"how many items to query in a batch" default:"2500" testDefault:"10000" `
2021-05-27 18:44:50 +01:00
2021-06-04 21:28:00 +01:00
AsOfSystemInterval time . Duration ` help:"as of system interval" releaseDefault:"-5m" devDefault:"-5m" `
2019-07-22 14:34:12 +01:00
}
2021-03-23 12:14:38 +00:00
// MetabaseDB contains iterators for the metabase data.
type MetabaseDB interface {
2021-05-07 10:05:51 +01:00
// Now returns the time on the database.
Now ( ctx context . Context ) ( time . Time , error )
2021-03-23 12:14:38 +00:00
// IterateLoopObjects iterates through all objects in metabase for metainfo loop purpose.
IterateLoopObjects ( ctx context . Context , opts metabase . IterateLoopObjects , fn func ( context . Context , metabase . LoopObjectsIterator ) error ) ( err error )
// IterateLoopStreams iterates through all streams passed in as arguments.
IterateLoopStreams ( ctx context . Context , opts metabase . IterateLoopStreams , handleStream func ( ctx context . Context , streamID uuid . UUID , next metabase . SegmentIterator ) error ) ( err error )
}
// Service is a metainfo loop service.
2019-09-10 14:24:16 +01:00
//
// architecture: Service
2021-03-23 12:14:38 +00:00
type Service struct {
config Config
2020-12-09 08:50:04 +00:00
metabaseDB MetabaseDB
2021-04-10 10:31:48 +01:00
join chan * observerContext
2020-12-09 08:50:04 +00:00
done chan struct { }
2019-07-22 14:34:12 +01:00
}
2021-03-23 12:14:38 +00:00
// New creates a new metainfo loop service.
func New ( config Config , metabaseDB MetabaseDB ) * Service {
return & Service {
2020-12-10 15:09:44 +00:00
metabaseDB : metabaseDB ,
config : config ,
2021-04-10 10:31:48 +01:00
join : make ( chan * observerContext ) ,
2020-12-10 15:09:44 +00:00
done : make ( chan struct { } ) ,
2019-07-22 14:34:12 +01:00
}
}
// Join will join the looper for one full cycle until completion and then returns.
2021-04-10 10:31:48 +01:00
// Joining will trigger a new iteration after coalesce duration.
2019-07-22 14:34:12 +01:00
// On ctx cancel the observer will return without completely finishing.
// Only on full complete iteration it will return nil.
// Safe to be called concurrently.
2021-04-10 10:31:48 +01:00
func ( loop * Service ) Join ( ctx context . Context , observer Observer ) ( err error ) {
return loop . joinObserver ( ctx , true , observer )
}
// Monitor will join the looper for one full cycle until completion and then returns.
// Joining with monitoring won't trigger after coalesce duration.
// On ctx cancel the observer will return without completely finishing.
// Only on full complete iteration it will return nil.
// Safe to be called concurrently.
func ( loop * Service ) Monitor ( ctx context . Context , observer Observer ) ( err error ) {
return loop . joinObserver ( ctx , false , observer )
}
// joinObserver will join the looper for one full cycle until completion and then returns.
// On ctx cancel the observer will return without completely finishing.
// Only on full complete iteration it will return nil.
// Safe to be called concurrently.
func ( loop * Service ) joinObserver ( ctx context . Context , trigger bool , obs Observer ) ( err error ) {
2019-07-22 14:34:12 +01:00
defer mon . Task ( ) ( & ctx ) ( & err )
2021-04-10 10:31:48 +01:00
obsctx := newObserverContext ( ctx , obs )
obsctx . trigger = trigger
2019-07-22 14:34:12 +01:00
select {
2021-04-10 10:31:48 +01:00
case loop . join <- obsctx :
2019-07-22 14:34:12 +01:00
case <- ctx . Done ( ) :
return ctx . Err ( )
case <- loop . done :
2021-03-23 12:14:38 +00:00
return ErrClosed
2019-07-22 14:34:12 +01:00
}
2021-04-10 10:31:48 +01:00
return obsctx . Wait ( )
2019-07-22 14:34:12 +01:00
}
// Run starts the looping service.
// It can only be called once, otherwise a panic will occur.
2021-03-23 12:14:38 +00:00
func ( loop * Service ) Run ( ctx context . Context ) ( err error ) {
2019-07-22 14:34:12 +01:00
defer mon . Task ( ) ( & ctx ) ( & err )
for {
2021-02-17 10:46:44 +00:00
err := loop . RunOnce ( ctx )
2019-07-22 14:34:12 +01:00
if err != nil {
return err
}
}
}
2019-09-23 20:14:39 +01:00
// Close closes the looping services.
2021-03-23 12:14:38 +00:00
func ( loop * Service ) Close ( ) ( err error ) {
2019-09-23 20:14:39 +01:00
close ( loop . done )
return nil
}
2021-04-22 10:07:18 +01:00
// monMetainfo is to preserve the monitoring names.
var monMetainfo = monkit . ScopeNamed ( "storj.io/storj/satellite/metainfo/metaloop" )
2021-02-17 10:46:44 +00:00
// RunOnce goes through metainfo one time and sends information to observers.
//
// It is not safe to call this concurrently with Run.
2021-03-23 12:14:38 +00:00
func ( loop * Service ) RunOnce ( ctx context . Context ) ( err error ) {
2021-04-22 10:07:18 +01:00
defer monMetainfo . Task ( ) ( & ctx ) ( & err ) //mon:locked
2019-07-22 14:34:12 +01:00
2021-04-10 10:31:48 +01:00
coalesceTimer := time . NewTimer ( loop . config . CoalesceDuration )
defer coalesceTimer . Stop ( )
2021-04-23 14:33:13 +01:00
stopTimer ( coalesceTimer )
2019-07-22 14:34:12 +01:00
2021-04-10 10:31:48 +01:00
earlyExit := make ( chan * observerContext )
earlyExitDone := make ( chan struct { } )
monitorEarlyExit := func ( obs * observerContext ) {
select {
case <- obs . ctx . Done ( ) :
select {
case <- earlyExitDone :
case earlyExit <- obs :
}
case <- earlyExitDone :
}
2019-07-22 14:34:12 +01:00
}
2021-04-10 10:31:48 +01:00
timerStarted := false
observers := [ ] * observerContext { }
2019-07-22 14:34:12 +01:00
waitformore :
for {
select {
2021-04-10 10:31:48 +01:00
// when the coalesce timer hits, we have waited enough for observers to join.
case <- coalesceTimer . C :
2019-07-22 14:34:12 +01:00
break waitformore
2021-04-10 10:31:48 +01:00
// wait for a new observer to join.
case obsctx := <- loop . join :
// when the observer triggers the loop and it's the first one,
// then start the coalescing timer.
if obsctx . trigger {
if ! timerStarted {
coalesceTimer . Reset ( loop . config . CoalesceDuration )
timerStarted = true
}
}
observers = append ( observers , obsctx )
go monitorEarlyExit ( obsctx )
// remove an observer from waiting when it's canceled before the loop starts.
case obsctx := <- earlyExit :
for i , obs := range observers {
if obs == obsctx {
observers = append ( observers [ : i ] , observers [ i + 1 : ] ... )
break
}
}
obsctx . HandleError ( obsctx . ctx . Err ( ) )
// reevalute, whether we acually need to start the loop.
timerShouldRun := false
for _ , obs := range observers {
timerShouldRun = timerShouldRun || obs . trigger
}
if ! timerShouldRun && timerStarted {
2021-04-23 14:33:13 +01:00
stopTimer ( coalesceTimer )
2021-04-10 10:31:48 +01:00
}
// when ctx done happens we can finish all the waiting observers.
2019-07-22 14:34:12 +01:00
case <- ctx . Done ( ) :
2021-04-10 10:31:48 +01:00
close ( earlyExitDone )
errorObservers ( observers , ctx . Err ( ) )
2019-07-22 14:34:12 +01:00
return ctx . Err ( )
}
}
2021-04-10 10:31:48 +01:00
close ( earlyExitDone )
2021-06-02 13:04:40 +01:00
return loop . iterateDatabase ( ctx , observers )
2019-11-18 15:26:48 +00:00
}
2021-04-23 14:33:13 +01:00
func stopTimer ( t * time . Timer ) {
t . Stop ( )
// drain if it contains something
select {
case <- t . C :
default :
}
}
2019-11-18 15:26:48 +00:00
// Wait waits for run to be finished.
// Safe to be called concurrently.
2021-03-23 12:14:38 +00:00
func ( loop * Service ) Wait ( ) {
2019-11-18 15:26:48 +00:00
<- loop . done
}
2021-06-02 09:42:33 +01:00
var errNoObservers = errs . New ( "no observers" )
2021-06-02 13:04:40 +01:00
func ( loop * Service ) iterateDatabase ( ctx context . Context , observers [ ] * observerContext ) ( err error ) {
2019-11-18 15:26:48 +00:00
defer func ( ) {
if err != nil {
2021-04-10 10:31:48 +01:00
errorObservers ( observers , err )
2019-11-18 15:26:48 +00:00
return
}
2020-12-15 21:44:57 +00:00
finishObservers ( observers )
2019-11-18 15:26:48 +00:00
} ( )
2021-06-02 13:04:40 +01:00
observers , err = loop . iterateObjects ( ctx , observers )
2021-06-02 09:42:33 +01:00
if errors . Is ( err , errNoObservers ) {
return nil
}
2021-02-18 11:37:49 +00:00
if err != nil {
2021-03-23 12:14:38 +00:00
return Error . Wrap ( err )
2020-12-10 15:09:44 +00:00
}
2021-02-18 11:37:49 +00:00
2021-06-02 09:42:33 +01:00
return nil
2020-12-10 15:09:44 +00:00
}
2021-06-02 13:04:40 +01:00
func ( loop * Service ) iterateObjects ( ctx context . Context , observers [ ] * observerContext ) ( _ [ ] * observerContext , err error ) {
2020-12-10 15:09:44 +00:00
defer mon . Task ( ) ( & ctx ) ( & err )
2021-06-02 13:04:40 +01:00
limit := loop . config . ListLimit
2021-02-23 16:41:46 +00:00
if limit <= 0 || limit > batchsizeLimit {
limit = batchsizeLimit
}
2021-06-02 13:04:40 +01:00
rateLimiter := rate . NewLimiter ( rate . Limit ( loop . config . RateLimit ) , 1 )
startingTime , err := loop . metabaseDB . Now ( ctx )
2021-05-07 10:05:51 +01:00
if err != nil {
return observers , Error . Wrap ( err )
}
2021-03-01 15:27:04 +00:00
2021-04-01 11:56:39 +01:00
observers = withObservers ( ctx , observers , func ( ctx context . Context , observer * observerContext ) bool {
err := observer . observer . LoopStarted ( ctx , LoopInfo { Started : startingTime } )
return ! observer . HandleError ( err )
} )
if len ( observers ) == 0 {
2021-06-02 09:42:33 +01:00
return observers , errNoObservers
2021-04-01 11:56:39 +01:00
}
2021-02-22 16:24:06 +00:00
// TODO we may consider keeping only expiration time as its
// only thing we need to handle segments
2021-03-01 14:29:03 +00:00
objectsMap := make ( map [ uuid . UUID ] metabase . LoopObjectEntry )
2021-02-22 16:24:06 +00:00
ids := make ( [ ] uuid . UUID , 0 , limit )
2021-03-30 21:24:35 +01:00
var objectsProcessed , segmentsProcessed int64
processBatch := func ( ctx context . Context ) ( err error ) {
defer mon . TaskNamed ( "processBatch" ) ( & ctx ) ( & err )
2021-02-22 16:24:06 +00:00
if len ( objectsMap ) == 0 {
return nil
}
2021-06-02 13:04:40 +01:00
err = loop . metabaseDB . IterateLoopStreams ( ctx , metabase . IterateLoopStreams {
2021-06-04 21:28:00 +01:00
StreamIDs : ids ,
AsOfSystemTime : startingTime ,
AsOfSystemInterval : loop . config . AsOfSystemInterval ,
2021-03-30 21:24:35 +01:00
} , func ( ctx context . Context , streamID uuid . UUID , next metabase . SegmentIterator ) ( err error ) {
defer mon . TaskNamed ( "iterateLoopStreamsCB" ) ( & ctx , "objs" , objectsProcessed , "segs" , segmentsProcessed ) ( & err )
2020-12-15 21:44:57 +00:00
if err := ctx . Err ( ) ; err != nil {
2020-12-10 15:09:44 +00:00
return err
2020-05-05 07:51:24 +01:00
}
2020-12-10 15:09:44 +00:00
2021-03-01 15:27:04 +00:00
obj , ok := objectsMap [ streamID ]
if ! ok {
return Error . New ( "unable to find corresponding object: %v" , streamID )
}
delete ( objectsMap , streamID )
2020-12-15 21:44:57 +00:00
2021-03-30 21:24:35 +01:00
observers = withObservers ( ctx , observers , func ( ctx context . Context , observer * observerContext ) bool {
2021-03-01 15:27:04 +00:00
object := Object ( obj )
2021-03-30 21:24:35 +01:00
return ! observer . HandleError ( handleObject ( ctx , observer , & object ) )
2021-02-22 16:24:06 +00:00
} )
if len ( observers ) == 0 {
2021-06-02 09:42:33 +01:00
return errNoObservers
2021-02-22 16:24:06 +00:00
}
2019-07-22 14:34:12 +01:00
2021-03-30 21:24:35 +01:00
objectsProcessed ++
2021-04-22 10:07:18 +01:00
monMetainfo . IntVal ( "objectsProcessed" ) . Observe ( objectsProcessed ) //mon:locked
2021-03-30 21:24:35 +01:00
2021-03-01 15:27:04 +00:00
for {
// if context has been canceled exit. Otherwise, continue
if err := ctx . Err ( ) ; err != nil {
return err
}
var segment metabase . LoopSegmentEntry
if ! next ( & segment ) {
break
}
location := metabase . SegmentLocation {
ProjectID : obj . ProjectID ,
BucketName : obj . BucketName ,
ObjectKey : obj . ObjectKey ,
Position : segment . Position ,
}
2021-03-30 21:24:35 +01:00
observers = withObservers ( ctx , observers , func ( ctx context . Context , observer * observerContext ) bool {
return ! observer . HandleError ( handleSegment ( ctx , observer , location , segment , obj . ExpiresAt ) )
2021-03-01 15:27:04 +00:00
} )
if len ( observers ) == 0 {
2021-06-02 09:42:33 +01:00
return errNoObservers
2021-03-01 15:27:04 +00:00
}
2021-03-30 21:24:35 +01:00
segmentsProcessed ++
2021-04-22 10:07:18 +01:00
monMetainfo . IntVal ( "segmentsProcessed" ) . Observe ( segmentsProcessed ) //mon:locked
2021-03-30 21:24:35 +01:00
2021-02-22 16:24:06 +00:00
}
2021-03-01 15:27:04 +00:00
return nil
} )
if err != nil {
return Error . Wrap ( err )
2021-02-22 16:24:06 +00:00
}
2021-03-01 15:27:04 +00:00
if len ( objectsMap ) > 0 {
return Error . New ( "unhandled objects %#v" , objectsMap )
}
2021-02-22 16:24:06 +00:00
return nil
}
2020-12-15 21:44:57 +00:00
2021-03-30 21:24:35 +01:00
var objectsIterated int64
2021-02-25 13:54:30 +00:00
segmentsInBatch := int32 ( 0 )
2021-06-02 13:04:40 +01:00
err = loop . metabaseDB . IterateLoopObjects ( ctx , metabase . IterateLoopObjects {
2021-06-04 21:28:00 +01:00
BatchSize : limit ,
AsOfSystemTime : startingTime ,
AsOfSystemInterval : loop . config . AsOfSystemInterval ,
2021-03-30 21:24:35 +01:00
} , func ( ctx context . Context , it metabase . LoopObjectsIterator ) ( err error ) {
defer mon . TaskNamed ( "iterateLoopObjectsCB" ) ( & ctx ) ( & err )
2021-03-01 14:29:03 +00:00
var entry metabase . LoopObjectEntry
2021-02-22 16:24:06 +00:00
for it . Next ( ctx , & entry ) {
2021-03-30 21:24:35 +01:00
timer := mon . Timer ( "iterateLoopObjectsRateLimit" ) . Start ( )
2021-02-22 16:24:06 +00:00
if err := rateLimiter . Wait ( ctx ) ; err != nil {
// We don't really execute concurrent batches so we should never
// exceed the burst size of 1 and this should never happen.
// We can also enter here if the context is cancelled.
2021-03-30 21:24:35 +01:00
timer . Stop ( )
2021-02-22 16:24:06 +00:00
return err
}
2021-03-30 21:24:35 +01:00
timer . Stop ( )
2021-04-22 10:07:18 +01:00
monMetainfo . IntVal ( "objectsIterated" ) . Observe ( objectsIterated ) //mon:locked
2021-06-03 18:13:12 +01:00
objectsIterated ++
2021-02-22 16:24:06 +00:00
objectsMap [ entry . StreamID ] = entry
ids = append ( ids , entry . StreamID )
2020-12-15 21:44:57 +00:00
2021-02-25 13:54:30 +00:00
// add +1 to reduce risk of crossing limit
segmentsInBatch += entry . SegmentCount + 1
if segmentsInBatch >= int32 ( limit ) {
2021-03-30 21:24:35 +01:00
err := processBatch ( ctx )
2021-02-22 16:24:06 +00:00
if err != nil {
return err
2020-12-15 21:44:57 +00:00
}
2019-07-22 14:34:12 +01:00
2021-02-22 16:24:06 +00:00
if len ( objectsMap ) > 0 {
return errs . New ( "objects map is not empty" )
2020-12-15 21:44:57 +00:00
}
2021-02-22 16:24:06 +00:00
ids = ids [ : 0 ]
2021-02-25 13:54:30 +00:00
segmentsInBatch = 0
2019-07-22 14:34:12 +01:00
}
2020-05-05 07:51:24 +01:00
}
2021-06-02 09:42:33 +01:00
return processBatch ( ctx )
2020-12-15 21:44:57 +00:00
} )
2020-12-10 15:09:44 +00:00
2020-12-15 21:44:57 +00:00
return observers , err
2020-12-10 15:09:44 +00:00
}
2021-03-30 21:24:35 +01:00
func withObservers ( ctx context . Context , observers [ ] * observerContext , handleObserver func ( ctx context . Context , observer * observerContext ) bool ) [ ] * observerContext {
defer mon . Task ( ) ( & ctx ) ( nil )
2021-02-22 16:24:06 +00:00
nextObservers := observers [ : 0 ]
for _ , observer := range observers {
2021-03-30 21:24:35 +01:00
keepObserver := handleObserver ( ctx , observer )
2021-02-22 16:24:06 +00:00
if keepObserver {
nextObservers = append ( nextObservers , observer )
}
}
return nextObservers
}
2021-03-30 21:24:35 +01:00
func handleObject ( ctx context . Context , observer * observerContext , object * Object ) ( err error ) {
defer mon . Task ( ) ( & ctx ) ( & err )
2020-12-10 15:09:44 +00:00
2021-03-30 21:24:35 +01:00
if err := observer . Object ( ctx , object ) ; err != nil {
return err
2020-12-10 15:09:44 +00:00
}
2021-03-30 21:24:35 +01:00
return observer . ctx . Err ( )
2020-12-10 15:09:44 +00:00
}
2021-03-30 21:24:35 +01:00
func handleSegment ( ctx context . Context , observer * observerContext , location metabase . SegmentLocation , segment metabase . LoopSegmentEntry , expirationDate * time . Time ) ( err error ) {
defer mon . Task ( ) ( & ctx ) ( & err )
2021-03-02 12:58:23 +00:00
2021-03-31 15:08:10 +01:00
loopSegment := & Segment {
Location : location ,
2021-03-02 12:58:23 +00:00
LoopSegmentEntry : segment ,
2020-12-10 20:49:23 +00:00
}
2021-03-02 12:58:23 +00:00
if expirationDate != nil {
loopSegment . ExpirationDate = * expirationDate
2020-12-10 15:09:44 +00:00
}
2021-03-02 12:58:23 +00:00
if loopSegment . Inline ( ) {
2021-03-30 21:24:35 +01:00
if err := observer . InlineSegment ( ctx , loopSegment ) ; err != nil {
return err
2020-12-10 15:09:44 +00:00
}
} else {
2021-03-30 21:24:35 +01:00
if err := observer . RemoteSegment ( ctx , loopSegment ) ; err != nil {
return err
2020-12-10 15:09:44 +00:00
}
}
2021-03-30 21:24:35 +01:00
return observer . ctx . Err ( )
2019-07-22 14:34:12 +01:00
}
2019-11-18 15:26:48 +00:00
func finishObservers ( observers [ ] * observerContext ) {
for _ , observer := range observers {
observer . Finish ( )
2019-07-22 14:34:12 +01:00
}
}
2021-04-10 10:31:48 +01:00
func errorObservers ( observers [ ] * observerContext , err error ) {
for _ , observer := range observers {
observer . HandleError ( err )
}
}