github.com/cs3org/reva/v2@v2.27.7/pkg/storage/utils/decomposedfs/tree/propagator/sync.go (about)

     1  // Copyright 2018-2021 CERN
     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  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package propagator
    20  
    21  import (
    22  	"context"
    23  	"errors"
    24  	"os"
    25  	"strconv"
    26  	"time"
    27  
    28  	"github.com/cs3org/reva/v2/pkg/appctx"
    29  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata"
    30  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata/prefixes"
    31  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node"
    32  	"github.com/rogpeppe/go-internal/lockedfile"
    33  	"github.com/rs/zerolog"
    34  )
    35  
    36  // SyncPropagator implements synchronous treetime & treesize propagation
    37  type SyncPropagator struct {
    38  	treeSizeAccounting bool
    39  	treeTimeAccounting bool
    40  	lookup             node.PathLookup
    41  }
    42  
    43  // NewSyncPropagator returns a new AsyncPropagator instance
    44  func NewSyncPropagator(treeSizeAccounting, treeTimeAccounting bool, lookup node.PathLookup) SyncPropagator {
    45  	return SyncPropagator{
    46  		treeSizeAccounting: treeSizeAccounting,
    47  		treeTimeAccounting: treeTimeAccounting,
    48  		lookup:             lookup,
    49  	}
    50  }
    51  
    52  // Propagate triggers a propagation
    53  func (p SyncPropagator) Propagate(ctx context.Context, n *node.Node, sizeDiff int64) error {
    54  	ctx, span := tracer.Start(ctx, "Propagate")
    55  	defer span.End()
    56  	sublog := appctx.GetLogger(ctx).With().
    57  		Str("method", "sync.Propagate").
    58  		Str("spaceid", n.SpaceID).
    59  		Str("nodeid", n.ID).
    60  		Int64("sizeDiff", sizeDiff).
    61  		Logger()
    62  
    63  	if !p.treeTimeAccounting && (!p.treeSizeAccounting || sizeDiff == 0) {
    64  		// no propagation enabled
    65  		sublog.Debug().Msg("propagation disabled or nothing to propagate")
    66  		return nil
    67  	}
    68  
    69  	// is propagation enabled for the parent node?
    70  	root := n.SpaceRoot
    71  
    72  	// use a sync time and don't rely on the mtime of the current node, as the stat might not change when a rename happened too quickly
    73  	sTime := time.Now().UTC()
    74  
    75  	// we loop until we reach the root
    76  	var (
    77  		err  error
    78  		stop bool
    79  	)
    80  
    81  	for err == nil && !stop && n.ID != root.ID {
    82  		n, stop, err = p.propagateItem(ctx, n, sTime, sizeDiff, sublog)
    83  	}
    84  
    85  	if err != nil {
    86  		sublog.Error().Err(err).Msg("error propagating")
    87  		return err
    88  	}
    89  	return nil
    90  }
    91  
    92  func (p SyncPropagator) propagateItem(ctx context.Context, n *node.Node, sTime time.Time, sizeDiff int64, log zerolog.Logger) (*node.Node, bool, error) {
    93  	log.Debug().Msg("propagating")
    94  
    95  	attrs := node.Attributes{}
    96  
    97  	var f *lockedfile.File
    98  	// lock parent before reading treesize or tree time
    99  
   100  	_, subspan := tracer.Start(ctx, "lockedfile.OpenFile")
   101  	parentFilename := p.lookup.MetadataBackend().LockfilePath(n.ParentPath())
   102  	f, err := lockedfile.OpenFile(parentFilename, os.O_RDWR|os.O_CREATE, 0600)
   103  	subspan.End()
   104  	if err != nil {
   105  		log.Error().Err(err).
   106  			Str("parent filename", parentFilename).
   107  			Msg("Propagation failed. Could not open metadata for parent with lock.")
   108  		return nil, true, err
   109  	}
   110  	// always log error if closing node fails
   111  	defer func() {
   112  		// ignore already closed error
   113  		cerr := f.Close()
   114  		if err == nil && cerr != nil && !errors.Is(cerr, os.ErrClosed) {
   115  			err = cerr // only overwrite err with en error from close if the former was nil
   116  		}
   117  	}()
   118  
   119  	if n, err = n.Parent(ctx); err != nil {
   120  		log.Error().Err(err).
   121  			Msg("Propagation failed. Could not read parent node.")
   122  		return n, true, err
   123  	}
   124  
   125  	if !n.HasPropagation(ctx) {
   126  		log.Debug().Str("attr", prefixes.PropagationAttr).Msg("propagation attribute not set or unreadable, not propagating")
   127  		// if the attribute is not set treat it as false / none / no propagation
   128  		return n, true, nil
   129  	}
   130  
   131  	log = log.With().Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Logger()
   132  
   133  	if p.treeTimeAccounting {
   134  		// update the parent tree time if it is older than the nodes mtime
   135  		updateSyncTime := false
   136  
   137  		var tmTime time.Time
   138  		tmTime, err = n.GetTMTime(ctx)
   139  		switch {
   140  		case err != nil:
   141  			// missing attribute, or invalid format, overwrite
   142  			log.Debug().Err(err).
   143  				Msg("could not read tmtime attribute, overwriting")
   144  			updateSyncTime = true
   145  		case tmTime.Before(sTime):
   146  			log.Debug().
   147  				Time("tmtime", tmTime).
   148  				Time("stime", sTime).
   149  				Msg("parent tmtime is older than node mtime, updating")
   150  			updateSyncTime = true
   151  		default:
   152  			log.Debug().
   153  				Time("tmtime", tmTime).
   154  				Time("stime", sTime).
   155  				Dur("delta", sTime.Sub(tmTime)).
   156  				Msg("parent tmtime is younger than node mtime, not updating")
   157  		}
   158  
   159  		if updateSyncTime {
   160  			// update the tree time of the parent node
   161  			attrs.SetString(prefixes.TreeMTimeAttr, sTime.UTC().Format(time.RFC3339Nano))
   162  		}
   163  
   164  		attrs.SetString(prefixes.TmpEtagAttr, "")
   165  	}
   166  
   167  	// size accounting
   168  	if p.treeSizeAccounting && sizeDiff != 0 {
   169  		var newSize uint64
   170  
   171  		// read treesize
   172  		treeSize, err := n.GetTreeSize(ctx)
   173  		switch {
   174  		case metadata.IsAttrUnset(err):
   175  			// fallback to calculating the treesize
   176  			log.Warn().Msg("treesize attribute unset, falling back to calculating the treesize")
   177  			newSize, err = calculateTreeSize(ctx, p.lookup, n.InternalPath())
   178  			if err != nil {
   179  				return n, true, err
   180  			}
   181  		case err != nil:
   182  			log.Error().Err(err).
   183  				Msg("Faild to propagate treesize change. Error when reading the treesize attribute from parent")
   184  			return n, true, err
   185  		case sizeDiff > 0:
   186  			newSize = treeSize + uint64(sizeDiff)
   187  		case uint64(-sizeDiff) > treeSize:
   188  			// The sizeDiff is larger than the current treesize. Which would result in
   189  			// a negative new treesize. Something must have gone wrong with the accounting.
   190  			// Reset the current treesize to 0.
   191  			log.Error().Uint64("treeSize", treeSize).Int64("sizeDiff", sizeDiff).
   192  				Msg("Error when updating treesize of parent node. Updated treesize < 0. Reestting to 0")
   193  			newSize = 0
   194  		default:
   195  			newSize = treeSize - uint64(-sizeDiff)
   196  		}
   197  
   198  		// update the tree size of the node
   199  		attrs.SetString(prefixes.TreesizeAttr, strconv.FormatUint(newSize, 10))
   200  		log.Debug().Uint64("newSize", newSize).Msg("updated treesize of parent node")
   201  	}
   202  
   203  	if err = n.SetXattrsWithContext(ctx, attrs, false); err != nil {
   204  		log.Error().Err(err).Msg("Failed to update extend attributes of parent node")
   205  		return n, true, err
   206  	}
   207  
   208  	return n, false, nil
   209  }