diff --git a/changelog/unreleased/ocis-synctime-accounting.md b/changelog/unreleased/ocis-synctime-accounting.md new file mode 100644 index 00000000000..2432e24fd7a --- /dev/null +++ b/changelog/unreleased/ocis-synctime-accounting.md @@ -0,0 +1,7 @@ +Enhancement: introduce ocis driver treetime accounting + +We added tree time accounting to the ocis storage driver which is modeled after [eos synctime accounting](http://eos-docs.web.cern.ch/eos-docs/configuration/namespace.html#enable-subtree-accounting). +It can be enabled using the new `treetime_accounting` option, which defaults to `false` +The `tmtime` is stored in an extended attribute `user.ocis.tmtime`. The treetime accounting is enabled for nodes which have the `user.ocis.propagation` extended attribute set to `"1"`. Currently, propagation is in sync. + +https://github.com/cs3org/reva/pull/1180 \ No newline at end of file diff --git a/pkg/storage/fs/ocis/node.go b/pkg/storage/fs/ocis/node.go index 645fbc0863c..97f463cb834 100644 --- a/pkg/storage/fs/ocis/node.go +++ b/pkg/storage/fs/ocis/node.go @@ -265,15 +265,8 @@ func (n *Node) AsResourceInfo(ctx context.Context) (ri *provider.ResourceInfo, e if _, err := io.WriteString(h, n.ID); err != nil { return nil, err } - var b []byte var tmTime time.Time - if b, err = xattr.Get(nodePath, treeMTimeAttr); err == nil { - if tmTime, err = time.Parse(time.RFC3339Nano, string(b)); err != nil { - // invalid format, overwrite - log.Error().Err(err).Interface("node", n).Str("tmtime", string(b)).Msg("invalid format, ignoring") - tmTime = fi.ModTime() - } - } else { + if tmTime, err = n.GetTMTime(); err != nil { // no tmtime, use mtime tmTime = fi.ModTime() } @@ -325,3 +318,39 @@ func (n *Node) AsResourceInfo(ctx context.Context) (ri *provider.ResourceInfo, e return ri, nil } + +// HasPropagation checks if the propagation attribute exists and is set to "1" +func (n *Node) HasPropagation() (propagation bool) { + nodePath := filepath.Join(n.pw.Root, "nodes", n.ID) + if b, err := xattr.Get(nodePath, propagationAttr); err == nil { + return string(b) == "1" + } + return false +} + +// GetTMTime reads the tmtime from the extended attributes +func (n *Node) GetTMTime() (tmTime time.Time, err error) { + nodePath := filepath.Join(n.pw.Root, "nodes", n.ID) + var b []byte + if b, err = xattr.Get(nodePath, treeMTimeAttr); err != nil { + return + } + return time.Parse(time.RFC3339Nano, string(b)) +} + +// SetTMTime writes the tmtime to the extended attributes +func (n *Node) SetTMTime(t time.Time) (err error) { + nodePath := filepath.Join(n.pw.Root, "nodes", n.ID) + return xattr.Set(nodePath, treeMTimeAttr, []byte(t.UTC().Format(time.RFC3339Nano))) +} + +// UnsetTempEtag removes the temporary etag attribute +func (n *Node) UnsetTempEtag() (err error) { + nodePath := filepath.Join(n.pw.Root, "nodes", n.ID) + if err = xattr.Remove(nodePath, tmpEtagAttr); err != nil { + if e, ok := err.(*xattr.Error); ok && e.Err.Error() == "no data available" { + return nil + } + } + return err +} diff --git a/pkg/storage/fs/ocis/tree.go b/pkg/storage/fs/ocis/tree.go index 6074f969fba..072b4a8cc70 100644 --- a/pkg/storage/fs/ocis/tree.go +++ b/pkg/storage/fs/ocis/tree.go @@ -312,70 +312,48 @@ func (t *Tree) Propagate(ctx context.Context, n *Node) (err error) { for err == nil && n.ID != root.ID { log.Debug().Interface("node", n).Msg("propagating") - parentPath := filepath.Join(t.pw.Root, "nodes", n.ParentID) + if n, err = n.Parent(); err != nil { + break + } // TODO none, sync and async? - var propagation string - if b, err = xattr.Get(parentPath, propagationAttr); err != nil { - if e, ok := err.(*xattr.Error); ok && e.Err.Error() == "no data available" { - log.Debug().Interface("node", n).Str("attr", propagationAttr).Msg("propagation attribute not set, not propagating") - // if the attribute is not set treat it as false / none / no propagation - return nil - } - log.Error().Interface("node", n).Msg("error reading propagation attribute") - return err - } - propagation = string(b) - if propagation != "1" { - log.Debug().Interface("node", n).Msg("propagation for the parent node disabled") - // propagation for this node disabled + if !n.HasPropagation() { + log.Debug().Interface("node", n).Str("attr", propagationAttr).Msg("propagation attribute not set or unreadable, not propagating") + // if the attribute is not set treat it as false / none / no propagation return nil } if t.pw.TreeTimeAccounting { // update the parent tree time if it is older than the nodes mtime updateSyncTime := false - if b, err = xattr.Get(parentPath, treeMTimeAttr); err != nil { - if e, ok := err.(*xattr.Error); ok && e.Err.Error() == "no data available" { - // attribute is not set, write it - log.Debug().Interface("node", n).Str("attr", treeMTimeAttr).Msg("tmtime attribute is not set, writing it") - err = nil - updateSyncTime = true - } else { - return err - } + + if tmTime, err := n.GetTMTime(); err != nil { + // missing attribute, or invalid format, overwrite + log.Error().Err(err).Interface("node", n).Msg("could not read tmtime attribute, overwriting") + updateSyncTime = true + } else if tmTime.Before(fi.ModTime()) { + log.Debug().Interface("node", n).Str("tmtime", string(b)).Str("mtime", fi.ModTime().UTC().Format(time.RFC3339Nano)).Msg("parent tmtime is older than node mtime, updating") + updateSyncTime = true } else { - if tmTime, err := time.Parse(time.RFC3339Nano, string(b)); err != nil { - // invalid format, overwrite - log.Error().Err(err).Interface("node", n).Str("tmtime", string(b)).Msg("invalid format, overwriting") - updateSyncTime = true - } else if tmTime.Before(fi.ModTime()) { - log.Debug().Interface("node", n).Str("tmtime", string(b)).Str("mtime", fi.ModTime().UTC().Format(time.RFC3339Nano)).Msg("parent tmtime is older than node mtime, updating") - updateSyncTime = true - } else { - log.Debug().Interface("node", n).Str("tmtime", string(b)).Str("mtime", fi.ModTime().UTC().Format(time.RFC3339Nano)).Msg("parent tmtime is younger than node mtime, not updating") - } + log.Debug().Interface("node", n).Str("tmtime", string(b)).Str("mtime", fi.ModTime().UTC().Format(time.RFC3339Nano)).Msg("parent tmtime is younger than node mtime, not updating") } if updateSyncTime { // update the tree time of the parent node - if err = xattr.Set(parentPath, treeMTimeAttr, []byte(fi.ModTime().UTC().Format(time.RFC3339Nano))); err != nil { + if err = n.SetTMTime(fi.ModTime()); err != nil { log.Error().Err(err).Interface("node", n).Time("tmtime", fi.ModTime().UTC()).Msg("could not update tmtime of parent node") return } log.Debug().Interface("node", n).Time("tmtime", fi.ModTime().UTC()).Msg("updated tmtime of parent node") } - if err := xattr.Remove(parentPath, tmpEtagAttr); err != nil { - if e, ok := err.(*xattr.Error); ok && e.Err.Error() != "no data available" { - log.Error().Interface("node", n).Str("attr", treeMTimeAttr).Msg("could not remove temporary etag attribute") - } + if err := n.UnsetTempEtag(); err != nil { + log.Error().Err(err).Interface("node", n).Msg("could not remove temporary etag attribute") } } // TODO size accounting - n, err = n.Parent() } if err != nil { log.Error().Err(err).Interface("node", n).Msg("error propagating")