go/storage/serialcachewriter.go (93 lines of code) (raw):
package storage
import (
"fmt"
"strings"
"github.com/golang/glog"
"github.com/mozilla/crlite/go"
)
const kSerials = "serials"
type SerialCacheWriter struct {
expDate types.ExpDate
issuer types.Issuer
cache RemoteCache
expirySet bool
}
func NewSerialCacheWriter(aExpDate types.ExpDate, aIssuer types.Issuer, aCache RemoteCache) *SerialCacheWriter {
return &SerialCacheWriter{
expDate: aExpDate,
issuer: aIssuer,
cache: aCache,
expirySet: false,
}
}
func (kc *SerialCacheWriter) id(params ...string) string {
return fmt.Sprintf("%s%s::%s", kc.expDate.ID(), strings.Join(params, ""), kc.issuer.ID())
}
func (kc *SerialCacheWriter) serialId(params ...string) string {
return fmt.Sprintf("%s::%s", kSerials, kc.id(params...))
}
// Returns true if this serial was unknown. Subsequent calls with the same serial
// will return false, as it will be known then.
func (kc *SerialCacheWriter) Insert(aSerial types.Serial) (bool, error) {
result, err := kc.cache.SetInsert(kc.serialId(), aSerial.BinaryString())
if err != nil {
return false, err
}
if !kc.expirySet {
kc.setExpiryFlag()
kc.expirySet = true
}
if result {
glog.V(3).Infof("[%s] Certificate unknown: %s", kc.id(), aSerial)
} else {
glog.V(3).Infof("[%s] Certificate already known: %s", kc.id(), aSerial)
}
return result, nil
}
func (kc *SerialCacheWriter) RemoveMany(aSerials []types.Serial) error {
// Removing an element of a set may leave the set empty. Redis
// automatically deletes empty sets, so assume that we need to reset
// the ExpireAt time for this set on the next Insert call.
kc.expirySet = false
serialStrings := make([]string, len(aSerials))
for i := 0; i < len(aSerials); i++ {
serialStrings[i] = aSerials[i].BinaryString()
}
return kc.cache.SetRemove(kc.serialId(), serialStrings)
}
func (kc *SerialCacheWriter) Count() int64 {
count, err := kc.cache.SetCardinality(kc.serialId())
if err != nil {
glog.Errorf("Couldn't determine count of %s, now at %d: %s", kc.id(), count, err)
}
return int64(count)
}
func (kc *SerialCacheWriter) Contains(aSerial types.Serial) (bool, error) {
return kc.cache.SetContains(kc.serialId(), aSerial.BinaryString())
}
func (kc *SerialCacheWriter) List() []types.Serial {
// Redis' scan methods regularly provide duplicates. The duplication
// happens at this level, pulling from SetToChan, so we make a hash-set
// here to de-duplicate when the memory impacts are the most minimal.
serials := make(map[string]struct{})
var count int
strChan := make(chan string)
go func() {
err := kc.cache.SetToChan(kc.serialId(), strChan)
if err != nil {
glog.Fatalf("Error obtaining list of known certificates: %v", err)
}
}()
for str := range strChan {
serials[str] = struct{}{}
count += 1
}
serialList := make([]types.Serial, 0, count)
for str := range serials {
bs, err := types.NewSerialFromBinaryString(str)
if err != nil {
glog.Errorf("Failed to populate serial str=[%s] %v", str, err)
continue
}
serialList = append(serialList, bs)
}
return serialList
}
func (kc *SerialCacheWriter) setExpiryFlag() {
expireTime := kc.expDate.ExpireTime()
if err := kc.cache.ExpireAt(kc.serialId(), expireTime); err != nil {
glog.Errorf("Couldn't set expiration time %v for serials %s: %v", expireTime, kc.id(), err)
}
}