satellite/metainfo: fix metainfo loop
This fix issues with passing observers between iteration methods. It's not best implementation but I think we will need to optimize it soon one way or another. Change-Id: I574599bfd10822d84e2d2f1800bcd88e176a76ea
This commit is contained in:
parent
ce20db9f68
commit
311b082838
@ -154,25 +154,6 @@ func (observer *observerContext) Wait() error {
|
|||||||
return <-observer.done
|
return <-observer.done
|
||||||
}
|
}
|
||||||
|
|
||||||
type observers []*observerContext
|
|
||||||
|
|
||||||
func (o *observers) Remove(toRemove *observerContext) {
|
|
||||||
list := *o
|
|
||||||
for i, observer := range list {
|
|
||||||
if observer == toRemove {
|
|
||||||
list[len(list)-1], list[i] = list[i], list[len(list)-1]
|
|
||||||
*o = list[:len(list)-1]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *observers) Finish() {
|
|
||||||
for _, observer := range *o {
|
|
||||||
observer.Finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoopConfig contains configurable values for the metainfo loop.
|
// LoopConfig contains configurable values for the metainfo loop.
|
||||||
type LoopConfig struct {
|
type LoopConfig struct {
|
||||||
CoalesceDuration time.Duration `help:"how long to wait for new observers before starting iteration" releaseDefault:"5s" devDefault:"5s"`
|
CoalesceDuration time.Duration `help:"how long to wait for new observers before starting iteration" releaseDefault:"5s" devDefault:"5s"`
|
||||||
@ -299,7 +280,7 @@ func (loop *Loop) Wait() {
|
|||||||
<-loop.done
|
<-loop.done
|
||||||
}
|
}
|
||||||
|
|
||||||
func iterateDatabase(ctx context.Context, db PointerDB, bucketsDB BucketsDB, metabaseDB MetabaseDB, observers observers, limit int, rateLimiter *rate.Limiter) (err error) {
|
func iterateDatabase(ctx context.Context, db PointerDB, bucketsDB BucketsDB, metabaseDB MetabaseDB, observers []*observerContext, limit int, rateLimiter *rate.Limiter) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
for _, observer := range observers {
|
for _, observer := range observers {
|
||||||
@ -307,7 +288,7 @@ func iterateDatabase(ctx context.Context, db PointerDB, bucketsDB BucketsDB, met
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
observers.Finish()
|
finishObservers(observers)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
more := true
|
more := true
|
||||||
@ -322,12 +303,17 @@ func iterateDatabase(ctx context.Context, db PointerDB, bucketsDB BucketsDB, met
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, bucket := range buckets.Items {
|
for _, bucket := range buckets.Items {
|
||||||
err := iterateObjects(ctx, bucket.ProjectID, bucket.Name, metabaseDB, observers, limit, rateLimiter)
|
observers, err = iterateObjects(ctx, bucket.ProjectID, bucket.Name, metabaseDB, observers, limit, rateLimiter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return LoopError.Wrap(err)
|
return LoopError.Wrap(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if context has been canceled exit. Otherwise, continue
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
more = buckets.More
|
more = buckets.More
|
||||||
if more {
|
if more {
|
||||||
lastBucket := buckets.Items[len(buckets.Items)-1]
|
lastBucket := buckets.Items[len(buckets.Items)-1]
|
||||||
@ -338,7 +324,7 @@ func iterateDatabase(ctx context.Context, db PointerDB, bucketsDB BucketsDB, met
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func iterateObjects(ctx context.Context, projectID uuid.UUID, bucket string, metabaseDB MetabaseDB, observers observers, limit int, rateLimiter *rate.Limiter) (err error) {
|
func iterateObjects(ctx context.Context, projectID uuid.UUID, bucket string, metabaseDB MetabaseDB, observers []*observerContext, limit int, rateLimiter *rate.Limiter) (_ []*observerContext, err error) {
|
||||||
defer mon.Task()(&ctx)(&err)
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
// TODO we should improve performance here, this is just most straightforward solution
|
// TODO we should improve performance here, this is just most straightforward solution
|
||||||
@ -359,6 +345,7 @@ func iterateObjects(ctx context.Context, projectID uuid.UUID, bucket string, met
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nextObservers := observers[:0]
|
||||||
for _, observer := range observers {
|
for _, observer := range observers {
|
||||||
location := metabase.ObjectLocation{
|
location := metabase.ObjectLocation{
|
||||||
ProjectID: projectID,
|
ProjectID: projectID,
|
||||||
@ -366,89 +353,77 @@ func iterateObjects(ctx context.Context, projectID uuid.UUID, bucket string, met
|
|||||||
ObjectKey: entry.ObjectKey,
|
ObjectKey: entry.ObjectKey,
|
||||||
}
|
}
|
||||||
keepObserver := handleObject(ctx, observer, location, entry)
|
keepObserver := handleObject(ctx, observer, location, entry)
|
||||||
if !keepObserver {
|
if keepObserver {
|
||||||
observers.Remove(observer)
|
nextObservers = append(nextObservers, observer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
observers = nextObservers
|
||||||
if len(observers) == 0 {
|
if len(observers) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// if context has been canceled exit. Otherwise, continue
|
// if context has been canceled exit. Otherwise, continue
|
||||||
select {
|
if err := ctx.Err(); err != nil {
|
||||||
case <-ctx.Done():
|
return err
|
||||||
return ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = iterateSegments(ctx, entry.StreamID, projectID, bucket, entry.ObjectKey, metabaseDB, observers, limit, rateLimiter, entry.ExpiresAt)
|
more := true
|
||||||
if err != nil {
|
cursor := metabase.SegmentPosition{}
|
||||||
return err
|
for more {
|
||||||
|
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.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
segments, err := metabaseDB.ListSegments(ctx, metabase.ListSegments{
|
||||||
|
StreamID: entry.StreamID,
|
||||||
|
Cursor: cursor,
|
||||||
|
Limit: limit,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, segment := range segments.Segments {
|
||||||
|
nextObservers := observers[:0]
|
||||||
|
location := metabase.SegmentLocation{
|
||||||
|
ProjectID: projectID,
|
||||||
|
BucketName: bucket,
|
||||||
|
ObjectKey: entry.ObjectKey,
|
||||||
|
Position: segment.Position,
|
||||||
|
}
|
||||||
|
for _, observer := range observers {
|
||||||
|
keepObserver := handleSegment(ctx, observer, location, segment, entry.ExpiresAt)
|
||||||
|
if keepObserver {
|
||||||
|
nextObservers = append(nextObservers, observer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
observers = nextObservers
|
||||||
|
if len(observers) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if context has been canceled exit. Otherwise, continue
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
more = segments.More
|
||||||
|
if more {
|
||||||
|
lastSegment := segments.Segments[len(segments.Segments)-1]
|
||||||
|
cursor = lastSegment.Position
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
return err
|
return observers, err
|
||||||
}
|
|
||||||
|
|
||||||
func iterateSegments(ctx context.Context, streamID uuid.UUID, projectID uuid.UUID, bucket string, objectKey metabase.ObjectKey, metabaseDB MetabaseDB, observers observers, limit int, rateLimiter *rate.Limiter, expireAt *time.Time) (err error) {
|
|
||||||
defer mon.Task()(&ctx)(&err)
|
|
||||||
|
|
||||||
more := true
|
|
||||||
cursor := metabase.SegmentPosition{}
|
|
||||||
for more {
|
|
||||||
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.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
segments, err := metabaseDB.ListSegments(ctx, metabase.ListSegments{
|
|
||||||
StreamID: streamID,
|
|
||||||
Cursor: cursor,
|
|
||||||
Limit: limit,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, segment := range segments.Segments {
|
|
||||||
for _, observer := range observers {
|
|
||||||
location := metabase.SegmentLocation{
|
|
||||||
ProjectID: projectID,
|
|
||||||
BucketName: bucket,
|
|
||||||
ObjectKey: objectKey,
|
|
||||||
Position: segment.Position,
|
|
||||||
}
|
|
||||||
keepObserver := handleSegment(ctx, observer, location, segment, expireAt)
|
|
||||||
if !keepObserver {
|
|
||||||
observers.Remove(observer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(observers) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// if context has been canceled exit. Otherwise, continue
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
more = segments.More
|
|
||||||
if more {
|
|
||||||
lastSegment := segments.Segments[len(segments.Segments)-1]
|
|
||||||
cursor = lastSegment.Position
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleObject(ctx context.Context, observer *observerContext, location metabase.ObjectLocation, object metabase.ObjectEntry) bool {
|
func handleObject(ctx context.Context, observer *observerContext, location metabase.ObjectLocation, object metabase.ObjectEntry) bool {
|
||||||
@ -486,7 +461,7 @@ func handleSegment(ctx context.Context, observer *observerContext, location meta
|
|||||||
}
|
}
|
||||||
|
|
||||||
loopSegment.StreamID = segment.StreamID
|
loopSegment.StreamID = segment.StreamID
|
||||||
loopSegment.DataSize = int(segment.EncryptedSize) // TODO should this be plain or enrypted size
|
loopSegment.DataSize = int(segment.EncryptedSize)
|
||||||
if segment.Inline() {
|
if segment.Inline() {
|
||||||
loopSegment.Inline = true
|
loopSegment.Inline = true
|
||||||
if observer.HandleError(observer.InlineSegment(ctx, loopSegment)) {
|
if observer.HandleError(observer.InlineSegment(ctx, loopSegment)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user