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 }