2017-05-11 14:39:54 +00:00
|
|
|
/*
|
|
|
|
Copyright 2015 Google Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
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{
|
2018-03-19 15:51:38 +00:00
|
|
|
Rule: &bttdpb.GcRule_Intersection_{Intersection: inter},
|
2017-05-11 14:39:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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{
|
2018-03-19 15:51:38 +00:00
|
|
|
Rule: &bttdpb.GcRule_Union_{Union: union},
|
2017-05-11 14:39:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2018-03-19 15:51:38 +00:00
|
|
|
return &bttdpb.GcRule{Rule: &bttdpb.GcRule_MaxNumVersions{MaxNumVersions: int32(mvp)}}
|
2017-05-11 14:39:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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{
|
2018-03-19 15:51:38 +00:00
|
|
|
Rule: &bttdpb.GcRule_MaxAge{MaxAge: &durpb.Duration{
|
2017-05-11 14:39:54 +00:00
|
|
|
Seconds: ns / 1e9,
|
|
|
|
Nanos: int32(ns % 1e9),
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
}
|
2017-07-23 07:51:42 +00:00
|
|
|
|
|
|
|
// GCRuleToString converts the given GcRule proto to a user-visible string.
|
|
|
|
func GCRuleToString(rule *bttdpb.GcRule) string {
|
|
|
|
if rule == nil {
|
|
|
|
return "<default>"
|
|
|
|
}
|
2018-01-16 13:20:59 +00:00
|
|
|
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 ""
|
2017-07-23 07:51:42 +00:00
|
|
|
}
|
2018-01-16 13:20:59 +00:00
|
|
|
}
|
2017-07-23 07:51:42 +00:00
|
|
|
|
2018-01-16 13:20:59 +00:00
|
|
|
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) + ")"
|
2017-07-23 07:51:42 +00:00
|
|
|
}
|