1
0
mirror of https://github.com/owncloud/ocis.git synced 2026-10-23 23:29:02 +03:00

chore: bump reva to fix/ocisdev-855-main for load-test comparison

This commit is contained in:
Michal Klos
2026-10-13 15:18:11 +02:00
parent aab12d018e
commit 2c84ed02f0
11 changed files with 126 additions and 16 deletions

2
go.mod
View File

@@ -67,7 +67,7 @@ require (
github.com/open-policy-agent/opa v1.21.1
github.com/orcaman/concurrent-map v1.0.0
github.com/owncloud/libre-graph-api-go v1.0.5-0.20260216101009-eeac018af245
github.com/owncloud/reva/v2 v2.0.0-20261012083840-8b649a4c3899
github.com/owncloud/reva/v2 v2.0.0-20261013125910-ed54ed63ad22
github.com/pkg/errors v0.9.1
github.com/pkg/xattr v0.4.12
github.com/prometheus/client_golang v1.24.1

4
go.sum
View File

@@ -736,8 +736,8 @@ github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HD
github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI=
github.com/owncloud/libre-graph-api-go v1.0.5-0.20260216101009-eeac018af245 h1:JRidLTAKhnvyLMRtVtSF4lhBa0NSAOs6fof+d6JnKII=
github.com/owncloud/libre-graph-api-go v1.0.5-0.20260216101009-eeac018af245/go.mod h1:z61VMGAJRtR1nbgXWiNoCkxUXP1B3Je9rMuJbnGd+Og=
github.com/owncloud/reva/v2 v2.0.0-20261012083840-8b649a4c3899 h1:IMu076AtnFEdabpBbowcGDYOeFLtm9ycNY1Z43YPYZU=
github.com/owncloud/reva/v2 v2.0.0-20261012083840-8b649a4c3899/go.mod h1:cgqg+UTe4ekjMbJj8hgoXSTxQ9fQramKcOUdvsFUrzc=
github.com/owncloud/reva/v2 v2.0.0-20261013125910-ed54ed63ad22 h1:/4AAImjYT1wJ0zCf3DWZS8GwiVWQKi2EQbjmmg07TNM=
github.com/owncloud/reva/v2 v2.0.0-20261013125910-ed54ed63ad22/go.mod h1:cgqg+UTe4ekjMbJj8hgoXSTxQ9fQramKcOUdvsFUrzc=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
github.com/pablodz/inotifywaitgo v0.0.12 h1:DVJJTFMDlSAdO46yisPF2vzozs/r+4k6ih8MVghq78M=

View File

@@ -463,6 +463,10 @@ func (s *Service) InitiateFileUpload(ctx context.Context, req *provider.Initiate
st = status.NewInsufficientStorage(ctx, err, "insufficient storage")
case errtypes.PreconditionFailed:
st = status.NewFailedPrecondition(ctx, err, "failed precondition")
case errtypes.Aborted:
st = status.NewAborted(ctx, err, "response from InitiateUpload")
case errtypes.AlreadyExists:
st = status.NewAlreadyExists(ctx, err, "response from InitiateUpload")
case errtypes.Locked:
st = status.NewLocked(ctx, "locked")
case errtypes.IsTooEarly:

View File

@@ -24,6 +24,7 @@ package errtypes
import (
"net/http"
"strconv"
"strings"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
@@ -213,6 +214,14 @@ func (e TooManyRequests) Error() string { return "error: too many requests: " +
// IsTooManyRequests implements the IsTooManyRequests interface
func (e TooManyRequests) IsTooManyRequests() {}
// Unavailable is the error to use when the upstream is temporarily unable to handle the request (502/503/504).
type Unavailable string
func (e Unavailable) Error() string { return "error: unavailable: " + string(e) }
// IsUnavailable implements the IsUnavailable interface.
func (e Unavailable) IsUnavailable() {}
// IsNotFound is the interface to implement
// to specify that a resource is not found.
type IsNotFound interface {
@@ -309,6 +318,12 @@ type IsTooManyRequests interface {
IsTooManyRequests()
}
// IsUnavailable is the interface to implement
// to specify that the upstream is temporarily unavailable.
type IsUnavailable interface {
IsUnavailable()
}
// ResourceProcessing is the error to use when a resource is currently being processed (e.g. an upload in progress) and the requested operation cannot proceed.
type ResourceProcessing string
@@ -395,8 +410,10 @@ func NewErrtypeFromHTTPStatusCode(code int, message string) error {
return TooManyRequests(message)
case StatusChecksumMismatch:
return ChecksumMismatch(message)
case http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout:
return Unavailable(message)
default:
return InternalError(message)
return InternalError("http " + strconv.Itoa(code) + ": " + message)
}
}
@@ -433,6 +450,8 @@ func NewHTTPStatusCodeFromErrtype(err error) int {
return http.StatusTooEarly
case TooManyRequests:
return http.StatusTooManyRequests
case Unavailable:
return http.StatusServiceUnavailable
case ChecksumMismatch:
return StatusChecksumMismatch
default:

View File

@@ -208,12 +208,15 @@ func (c *Cache) Add(ctx context.Context, storageID, spaceID, shareID string, sha
// CS3 uses an already exists error instead of precondition failed when using an If-None-Match=* header / IfExists flag in the InitiateFileUpload call.
// Thas happens when the cache thinks there is no file.
// continue with sync below
case errtypes.TooEarly:
log.Debug().Msg("too early when persisting added provider share: write-lock contention. retrying...")
// continue with sync below
default:
span.SetStatus(codes.Error, fmt.Sprintf("persisting added provider share failed. giving up: %s", err.Error()))
log.Error().Err(err).Msg("persisting added provider share failed")
return err
}
if err := c.syncWithLock(ctx, storageID, spaceID); err != nil {
if err := c.resyncAfterConflict(ctx, storageID, spaceID); err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
log.Error().Err(err).Msg("persisting added provider share failed. giving up.")
@@ -276,12 +279,15 @@ func (c *Cache) Remove(ctx context.Context, storageID, spaceID, shareID string)
log.Debug().Msg("precondition failed when persisting removed provider share: etag changed. retrying...")
// actually, this is the wrong status code and we treat it like errtypes.Aborted because of inconsistencies on the server side
// continue with sync below
case errtypes.TooEarly:
log.Debug().Msg("too early when persisting removed provider share: write-lock contention. retrying...")
// continue with sync below
default:
span.SetStatus(codes.Error, fmt.Sprintf("persisting removed provider share failed. giving up: %s", err.Error()))
log.Error().Err(err).Msg("persisting removed provider share failed")
return err
}
if err := c.syncWithLock(ctx, storageID, spaceID); err != nil {
if err := c.resyncAfterConflict(ctx, storageID, spaceID); err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
log.Error().Err(err).Msg("persisting removed provider share failed. giving up.")
@@ -479,6 +485,16 @@ func (c *Cache) PurgeSpace(ctx context.Context, storageID, spaceID string) error
}
func (c *Cache) syncWithLock(ctx context.Context, storageID, spaceID string) error {
return c.doSync(ctx, storageID, spaceID, true)
}
// Unlike syncWithLock, a NotFound here must not reset the cache -- a pending
// mutation is about to overwrite it, dropping a sibling writer's data.
func (c *Cache) resyncAfterConflict(ctx context.Context, storageID, spaceID string) error {
return c.doSync(ctx, storageID, spaceID, false)
}
func (c *Cache) doSync(ctx context.Context, storageID, spaceID string, resetOnNotFound bool) error {
ctx, span := tracer.Start(ctx, "syncWithLock")
defer span.End()
@@ -502,6 +518,13 @@ func (c *Cache) syncWithLock(ctx context.Context, storageID, spaceID string) err
case nil:
span.AddEvent("updating local cache")
case errtypes.NotFound:
// No prior etag means this is a bootstrap race, not a lost update -- safe to reset.
if !resetOnNotFound && space.Etag != "" {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
log.Error().Err(err).Msg("lost update: backing file disappeared mid-retry")
return err
}
span.SetStatus(codes.Ok, "")
if err := c.Persist(ctx, storageID, spaceID); err != nil {
log.Warn().Err(err).Msg("failed to create empty provider cache file")

View File

@@ -223,10 +223,12 @@ func (c *Cache) List(ctx context.Context, userID string) (map[string]*Space, err
return spaces, nil
}
// isSyncTransient reports whether a resync's Download error is worth retrying.
// errtypes.InternalError is excluded on purpose -- retrying a permanent failure just burns the budget.
func isSyncTransient(err error) bool {
_, isTooEarly := err.(errtypes.IsTooEarly)
_, isInternal := err.(errtypes.IsInternalError)
return isTooEarly || isInternal
_, isUnavailable := err.(errtypes.IsUnavailable)
return isTooEarly || isUnavailable
}
func (c *Cache) retryPersist(ctx context.Context, userID, spaceID string, persistFunc func() error) error {
@@ -274,12 +276,23 @@ func (c *Cache) retryPersist(ctx context.Context, userID, spaceID string, persis
timer.Stop()
return ctx.Err()
}
if serr := c.syncIfStale(ctx, userID); serr != nil {
if !isSyncTransient(serr) {
log.Error().Int("attempt", attempt).Err(serr).Msg("lost update: re-read failed, aborting")
return serr
serr := c.resyncAfterConflict(ctx, userID)
// Retrying persistFunc now would just repeat the same conflict against stale
// state -- keep retrying the resync itself until it refreshes that state.
for resyncAttempt := 0; serr != nil && isSyncTransient(serr) && resyncAttempt < 20; resyncAttempt++ {
log.Warn().Int("attempt", attempt).Int("resyncAttempt", resyncAttempt).Err(serr).Msg("lost update: re-read before retry")
timer := time.NewTimer(expBackoff(resyncAttempt))
select {
case <-timer.C:
case <-ctx.Done():
timer.Stop()
return ctx.Err()
}
log.Warn().Int("attempt", attempt).Err(serr).Msg("lost update: re-read before retry")
serr = c.resyncAfterConflict(ctx, userID)
}
if serr != nil {
log.Error().Int("attempt", attempt).Err(serr).Msg("lost update: re-read failed, aborting")
return serr
}
}
return err
@@ -287,6 +300,16 @@ func (c *Cache) retryPersist(ctx context.Context, userID, spaceID string, persis
// syncIfStale pulls the authoritative state from storage when the local replica is stale; caller must hold the user lock.
func (c *Cache) syncIfStale(ctx context.Context, userID string) error {
return c.doSync(ctx, userID, true)
}
// Unlike syncIfStale, a NotFound here must not reset the cache -- a pending
// mutation is about to overwrite it, dropping a sibling writer's data.
func (c *Cache) resyncAfterConflict(ctx context.Context, userID string) error {
return c.doSync(ctx, userID, false)
}
func (c *Cache) doSync(ctx context.Context, userID string, resetOnNotFound bool) error {
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "SyncIfStale")
defer span.End()
span.SetAttributes(attribute.String("cs3.userid", userID))
@@ -299,6 +322,7 @@ func (c *Cache) syncIfStale(ctx context.Context, userID string) error {
span.AddEvent("updating cache")
// - update cached list of created shares for the user in memory if changed
rss, _ := c.ReceivedSpaces.Load(userID)
hadEtag := rss.etag != ""
dlres, err := c.storage.Download(ctx, metadata.DownloadRequest{
Path: jsonPath,
IfNoneMatch: []string{rss.etag},
@@ -307,6 +331,12 @@ func (c *Cache) syncIfStale(ctx context.Context, userID string) error {
case nil:
span.AddEvent("updating local cache")
case errtypes.NotFound:
// No prior etag means this is a bootstrap race, not a lost update -- safe to reset.
if !resetOnNotFound && hadEtag {
span.SetStatus(codes.Error, err.Error())
log.Error().Err(err).Msg("lost update: backing file disappeared mid-retry")
return err
}
span.SetStatus(codes.Ok, "")
if err := c.persist(ctx, userID); err != nil {
log.Warn().Err(err).Msg("failed to create empty received share cache file")

View File

@@ -144,12 +144,15 @@ func (c *Cache) Add(ctx context.Context, userid, shareID string) error {
// CS3 uses an already exists error instead of precondition failed when using an If-None-Match=* header / IfExists flag in the InitiateFileUpload call.
// Thas happens when the cache thinks there is no file.
// continue with sync below
case errtypes.TooEarly:
log.Debug().Msg("too early when persisting added share: write-lock contention. retrying...")
// continue with sync below
default:
span.SetStatus(codes.Error, fmt.Sprintf("persisting added share failed. giving up: %s", err.Error()))
log.Error().Err(err).Msg("persisting added share failed")
return err
}
if err := c.syncWithLock(ctx, userid); err != nil {
if err := c.resyncAfterConflict(ctx, userid); err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
log.Error().Err(err).Msg("persisting added share failed. giving up.")
@@ -219,12 +222,15 @@ func (c *Cache) Remove(ctx context.Context, userid, shareID string) error {
// CS3 uses an already exists error instead of precondition failed when using an If-None-Match=* header / IfExists flag in the InitiateFileUpload call.
// Thas happens when the cache thinks there is no file.
// continue with sync below
case errtypes.TooEarly:
log.Debug().Msg("too early when persisting removed share: write-lock contention. retrying...")
// continue with sync below
default:
span.SetStatus(codes.Error, fmt.Sprintf("persisting removed share failed. giving up: %s", err.Error()))
log.Error().Err(err).Msg("persisting removed share failed")
return err
}
if err := c.syncWithLock(ctx, userid); err != nil {
if err := c.resyncAfterConflict(ctx, userid); err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return err
@@ -260,6 +266,16 @@ func (c *Cache) List(ctx context.Context, userid string) (map[string]SpaceShareI
}
func (c *Cache) syncWithLock(ctx context.Context, userID string) error {
return c.doSync(ctx, userID, true)
}
// Unlike syncWithLock, a NotFound here must not reset the cache -- a pending
// mutation is about to overwrite it, dropping a sibling writer's data.
func (c *Cache) resyncAfterConflict(ctx context.Context, userID string) error {
return c.doSync(ctx, userID, false)
}
func (c *Cache) doSync(ctx context.Context, userID string, resetOnNotFound bool) error {
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Sync")
defer span.End()
span.SetAttributes(attribute.String("cs3.userid", userID))
@@ -274,7 +290,9 @@ func (c *Cache) syncWithLock(ctx context.Context, userID string) error {
dlreq := metadata.DownloadRequest{
Path: userCreatedPath,
}
var hadEtag bool
if us, ok := c.UserShares.Load(userID); ok && us.Etag != "" {
hadEtag = true
dlreq.IfNoneMatch = []string{us.Etag}
}
@@ -283,6 +301,12 @@ func (c *Cache) syncWithLock(ctx context.Context, userID string) error {
case nil:
span.AddEvent("updating local cache")
case errtypes.NotFound:
// No prior etag means this is a bootstrap race, not a lost update -- safe to reset.
if !resetOnNotFound && hadEtag {
span.SetStatus(codes.Error, err.Error())
log.Error().Err(err).Msg("lost update: backing file disappeared mid-retry")
return err
}
span.SetStatus(codes.Ok, "")
if err := c.Persist(ctx, userID); err != nil {
log.Warn().Err(err).Msg("failed to create empty share cache file")

View File

@@ -26,6 +26,7 @@ import (
"path/filepath"
"strings"
"github.com/owncloud/reva/v2/pkg/errtypes"
"github.com/owncloud/reva/v2/pkg/storage/utils/decomposedfs/lookup"
"github.com/owncloud/reva/v2/pkg/storage/utils/decomposedfs/node"
"github.com/owncloud/reva/v2/pkg/utils"
@@ -155,6 +156,9 @@ func (bs *Blobstore) Download(node *node.Node) (io.ReadCloser, error) {
dest := bs.Path(node)
file, err := os.Open(dest)
if err != nil {
if os.IsNotExist(err) {
return nil, errtypes.NotFound(dest)
}
return nil, errors.Wrapf(err, "could not read blob '%s'", dest)
}
return file, nil

View File

@@ -882,6 +882,9 @@ func (fs *Decomposedfs) Download(ctx context.Context, ref *provider.Reference, o
if openReaderFunc(ri) {
reader, err = fs.tp.ReadBlob(n)
if err != nil {
if _, ok := err.(errtypes.IsNotFound); ok {
return nil, nil, err
}
return nil, nil, errors.Wrap(err, "Decomposedfs: error download blob '"+n.ID+"'")
}
}

View File

@@ -183,6 +183,9 @@ func (fs *Decomposedfs) DownloadRevision(ctx context.Context, ref *provider.Refe
if openReaderFunc(ri) {
reader, err = fs.tp.ReadBlob(&revisionNode)
if err != nil {
if _, ok := err.(errtypes.IsNotFound); ok {
return nil, nil, err
}
return nil, nil, errors.Wrapf(err, "Decomposedfs: could not download blob of revision '%s' for node '%s'", n.ID, revisionKey)
}
}

2
vendor/modules.txt vendored
View File

@@ -1321,7 +1321,7 @@ github.com/orcaman/concurrent-map
# github.com/owncloud/libre-graph-api-go v1.0.5-0.20260216101009-eeac018af245
## explicit; go 1.18
github.com/owncloud/libre-graph-api-go
# github.com/owncloud/reva/v2 v2.0.0-20261012083840-8b649a4c3899
# github.com/owncloud/reva/v2 v2.0.0-20261013125910-ed54ed63ad22
## explicit; go 1.26.0
github.com/owncloud/reva/v2/cmd/revad/internal/grace
github.com/owncloud/reva/v2/cmd/revad/runtime