| /* |
| Copyright 2015 Google LLC |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package bigtable |
| |
| import ( |
| "fmt" |
| "strings" |
| "time" |
| |
| durpb "github.com/golang/protobuf/ptypes/duration" |
| bttdpb "google.golang.org/genproto/googleapis/bigtable/admin/v2" |
| ) |
| |
| // A GCPolicy represents a rule that determines which cells are eligible for garbage collection. |
| type GCPolicy interface { |
| String() string |
| proto() *bttdpb.GcRule |
| } |
| |
| // IntersectionPolicy returns a GC policy that only applies when all its sub-policies apply. |
| func IntersectionPolicy(sub ...GCPolicy) GCPolicy { return intersectionPolicy{sub} } |
| |
| type intersectionPolicy struct { |
| sub []GCPolicy |
| } |
| |
| func (ip intersectionPolicy) String() string { |
| var ss []string |
| for _, sp := range ip.sub { |
| ss = append(ss, sp.String()) |
| } |
| return "(" + strings.Join(ss, " && ") + ")" |
| } |
| |
| func (ip intersectionPolicy) proto() *bttdpb.GcRule { |
| inter := &bttdpb.GcRule_Intersection{} |
| for _, sp := range ip.sub { |
| inter.Rules = append(inter.Rules, sp.proto()) |
| } |
| return &bttdpb.GcRule{ |
| Rule: &bttdpb.GcRule_Intersection_{Intersection: inter}, |
| } |
| } |
| |
| // UnionPolicy returns a GC policy that applies when any of its sub-policies apply. |
| func UnionPolicy(sub ...GCPolicy) GCPolicy { return unionPolicy{sub} } |
| |
| type unionPolicy struct { |
| sub []GCPolicy |
| } |
| |
| func (up unionPolicy) String() string { |
| var ss []string |
| for _, sp := range up.sub { |
| ss = append(ss, sp.String()) |
| } |
| return "(" + strings.Join(ss, " || ") + ")" |
| } |
| |
| func (up unionPolicy) proto() *bttdpb.GcRule { |
| union := &bttdpb.GcRule_Union{} |
| for _, sp := range up.sub { |
| union.Rules = append(union.Rules, sp.proto()) |
| } |
| return &bttdpb.GcRule{ |
| Rule: &bttdpb.GcRule_Union_{Union: union}, |
| } |
| } |
| |
| // MaxVersionsPolicy returns a GC policy that applies to all versions of a cell |
| // except for the most recent n. |
| func MaxVersionsPolicy(n int) GCPolicy { return maxVersionsPolicy(n) } |
| |
| type maxVersionsPolicy int |
| |
| func (mvp maxVersionsPolicy) String() string { return fmt.Sprintf("versions() > %d", int(mvp)) } |
| |
| func (mvp maxVersionsPolicy) proto() *bttdpb.GcRule { |
| return &bttdpb.GcRule{Rule: &bttdpb.GcRule_MaxNumVersions{MaxNumVersions: int32(mvp)}} |
| } |
| |
| // MaxAgePolicy returns a GC policy that applies to all cells |
| // older than the given age. |
| func MaxAgePolicy(d time.Duration) GCPolicy { return maxAgePolicy(d) } |
| |
| type maxAgePolicy time.Duration |
| |
| var units = []struct { |
| d time.Duration |
| suffix string |
| }{ |
| {24 * time.Hour, "d"}, |
| {time.Hour, "h"}, |
| {time.Minute, "m"}, |
| } |
| |
| func (ma maxAgePolicy) String() string { |
| d := time.Duration(ma) |
| for _, u := range units { |
| if d%u.d == 0 { |
| return fmt.Sprintf("age() > %d%s", d/u.d, u.suffix) |
| } |
| } |
| return fmt.Sprintf("age() > %d", d/time.Microsecond) |
| } |
| |
| func (ma maxAgePolicy) proto() *bttdpb.GcRule { |
| // This doesn't handle overflows, etc. |
| // Fix this if people care about GC policies over 290 years. |
| ns := time.Duration(ma).Nanoseconds() |
| return &bttdpb.GcRule{ |
| Rule: &bttdpb.GcRule_MaxAge{MaxAge: &durpb.Duration{ |
| Seconds: ns / 1e9, |
| Nanos: int32(ns % 1e9), |
| }}, |
| } |
| } |
| |
| type noGCPolicy struct{} |
| |
| func (n noGCPolicy) String() string { return "" } |
| |
| func (n noGCPolicy) proto() *bttdpb.GcRule { return &bttdpb.GcRule{Rule: nil} } |
| |
| // NoGcPolicy applies to all cells setting maxage and maxversions to nil implies no gc policies |
| func NoGcPolicy() GCPolicy { return noGCPolicy{} } |
| |
| // GCRuleToString converts the given GcRule proto to a user-visible string. |
| func GCRuleToString(rule *bttdpb.GcRule) string { |
| if rule == nil { |
| return "<never>" |
| } |
| switch r := rule.Rule.(type) { |
| case *bttdpb.GcRule_MaxNumVersions: |
| return MaxVersionsPolicy(int(r.MaxNumVersions)).String() |
| case *bttdpb.GcRule_MaxAge: |
| return MaxAgePolicy(time.Duration(r.MaxAge.Seconds) * time.Second).String() |
| case *bttdpb.GcRule_Intersection_: |
| return joinRules(r.Intersection.Rules, " && ") |
| case *bttdpb.GcRule_Union_: |
| return joinRules(r.Union.Rules, " || ") |
| default: |
| return "" |
| } |
| } |
| |
| func joinRules(rules []*bttdpb.GcRule, sep string) string { |
| var chunks []string |
| for _, r := range rules { |
| chunks = append(chunks, GCRuleToString(r)) |
| } |
| return "(" + strings.Join(chunks, sep) + ")" |
| } |