github.com/mre-fog/trillianxx@v1.1.2-0.20180615153820-ae375a99d36a/server/admin/tree_gc.go (about)

     1  // Copyright 2017 Google Inc. All Rights Reserved.
     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  package admin
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"errors"
    21  	"fmt"
    22  	"math/rand"
    23  	"sync"
    24  	"time"
    25  
    26  	"github.com/golang/glog"
    27  	"github.com/golang/protobuf/ptypes"
    28  	"github.com/google/trillian/monitoring"
    29  	"github.com/google/trillian/storage"
    30  )
    31  
    32  const (
    33  	deleteErrReason        = "delete_error"
    34  	timestampParseErrReson = "timestamp_parse_error"
    35  )
    36  
    37  var (
    38  	timeNow   = time.Now
    39  	timeSleep = time.Sleep
    40  
    41  	hardDeleteCounter monitoring.Counter
    42  	metricsOnce       sync.Once
    43  )
    44  
    45  func incHardDeleteCounter(treeID int64, success bool, reason string) {
    46  	hardDeleteCounter.Inc(fmt.Sprint(treeID), fmt.Sprint(success), reason)
    47  }
    48  
    49  // DeletedTreeGC garbage collects deleted trees.
    50  //
    51  // Tree deletion goes through two separate stages:
    52  // * Soft deletion, which "flips a bit" (see Tree.Deleted) but otherwise leaves the tree unaltered
    53  // * Hard deletion, which effectively removes all tree data
    54  //
    55  // DeletedTreeGC performs the transition from soft to hard deletion. Trees that have been deleted
    56  // for at least DeletedThreshold are eligible for garbage collection.
    57  type DeletedTreeGC struct {
    58  	// admin is the storage.AdminStorage interface.
    59  	admin storage.AdminStorage
    60  
    61  	// deleteThreshold defines the minimum time a tree has to remain in the soft-deleted state
    62  	// before it's eligible for garbage collection.
    63  	deleteThreshold time.Duration
    64  
    65  	// minRunInterval defines how frequently sweeps for deleted trees are performed.
    66  	// Actual runs happen randomly between [minInterval,2*minInterval).
    67  	minRunInterval time.Duration
    68  }
    69  
    70  // NewDeletedTreeGC returns a new DeletedTreeGC.
    71  func NewDeletedTreeGC(admin storage.AdminStorage, threshold, minRunInterval time.Duration, mf monitoring.MetricFactory) *DeletedTreeGC {
    72  	gc := &DeletedTreeGC{
    73  		admin:           admin,
    74  		deleteThreshold: threshold,
    75  		minRunInterval:  minRunInterval,
    76  	}
    77  	metricsOnce.Do(func() {
    78  		if mf == nil {
    79  			mf = monitoring.InertMetricFactory{}
    80  		}
    81  		hardDeleteCounter = mf.NewCounter("tree_hard_delete_counter", "Counter of hard-deleted trees", monitoring.TreeIDLabel, "success", "reason")
    82  	})
    83  	return gc
    84  }
    85  
    86  // Run starts the tree garbage collection process. It runs until ctx is cancelled.
    87  func (gc *DeletedTreeGC) Run(ctx context.Context) {
    88  	for {
    89  		select {
    90  		case <-ctx.Done():
    91  			return
    92  		default:
    93  		}
    94  
    95  		count, err := gc.RunOnce(ctx)
    96  		if err != nil {
    97  			glog.Errorf("DeletedTreeGC.Run: %v", err)
    98  		}
    99  		if count > 0 {
   100  			glog.Infof("DeletedTreeGC.Run: successfully deleted %v trees", count)
   101  		}
   102  
   103  		d := gc.minRunInterval + time.Duration(rand.Int63n(gc.minRunInterval.Nanoseconds()))
   104  		timeSleep(d)
   105  	}
   106  }
   107  
   108  // RunOnce performs a single tree garbage collection sweep. Returns the number of successfully
   109  // deleted trees.
   110  //
   111  // It attempts to delete as many eligible trees as possible, regardless of failures. If it
   112  // encounters any failures while deleting the resulting error is non-nil.
   113  func (gc *DeletedTreeGC) RunOnce(ctx context.Context) (int, error) {
   114  	now := timeNow()
   115  
   116  	// List and delete trees in separate transactions. Hard-deletes may cascade to a lot of data, so
   117  	// each delete should be in its own transaction as well.
   118  	// It's OK to list and delete separately because HardDelete does its own state checking, plus
   119  	// deleted trees are unlikely to change, specially those deleted for a while.
   120  	trees, err := storage.ListTrees(ctx, gc.admin, true /* includeDeleted */)
   121  	if err != nil {
   122  		return 0, fmt.Errorf("error listing trees: %v", err)
   123  	}
   124  
   125  	count := 0
   126  	var errs []error
   127  	for _, tree := range trees {
   128  		if !tree.Deleted {
   129  			continue
   130  		}
   131  		deleteTime, err := ptypes.Timestamp(tree.DeleteTime)
   132  		if err != nil {
   133  			errs = append(errs, fmt.Errorf("error parsing delete_time of tree %v: %v", tree.TreeId, err))
   134  			incHardDeleteCounter(tree.TreeId, false, timestampParseErrReson)
   135  			continue
   136  		}
   137  		durationSinceDelete := now.Sub(deleteTime)
   138  		if durationSinceDelete <= gc.deleteThreshold {
   139  			continue
   140  		}
   141  
   142  		glog.Infof("DeletedTreeGC.RunOnce: Hard-deleting tree %v after %v", tree.TreeId, durationSinceDelete)
   143  		if err := storage.HardDeleteTree(ctx, gc.admin, tree.TreeId); err != nil {
   144  			errs = append(errs, fmt.Errorf("error hard-deleting tree %v: %v", tree.TreeId, err))
   145  			incHardDeleteCounter(tree.TreeId, false, deleteErrReason)
   146  			continue
   147  		}
   148  
   149  		count++
   150  		incHardDeleteCounter(tree.TreeId, true, "")
   151  	}
   152  
   153  	if len(errs) == 0 {
   154  		return count, nil
   155  	}
   156  
   157  	buf := &bytes.Buffer{}
   158  	buf.WriteString("encountered errors hard-deleting trees:")
   159  	for _, err := range errs {
   160  		buf.WriteString("\n\t")
   161  		buf.WriteString(err.Error())
   162  	}
   163  	return count, errors.New(buf.String())
   164  }