github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/teams/storage/merkle.go (about)

     1  package storage
     2  
     3  import (
     4  	"fmt"
     5  	lru "github.com/hashicorp/golang-lru"
     6  	"github.com/keybase/client/go/libkb"
     7  	"github.com/keybase/client/go/protocol/keybase1"
     8  	"log"
     9  	"sync"
    10  )
    11  
    12  // Merkle stores when a team last polled merkle. Threadsafe.
    13  type Merkle struct {
    14  	sync.Mutex
    15  	lru *lru.Cache
    16  }
    17  
    18  // Increment to invalidate the disk cache.
    19  const merkleDiskStorageVersion = 1
    20  const merkleMemCacheLRUSize = 2000
    21  
    22  type merkleDiskStorageItem struct {
    23  	Version  int           `codec:"V"`
    24  	PolledAt keybase1.Time `codec:"T"`
    25  }
    26  
    27  func NewMerkle() *Merkle {
    28  	nlru, err := lru.New(merkleMemCacheLRUSize)
    29  	if err != nil {
    30  		// lru.New only panics if size <= 0
    31  		log.Panicf("Could not create lru cache: %v", err)
    32  	}
    33  	return &Merkle{
    34  		lru: nlru,
    35  	}
    36  }
    37  
    38  func merkleKey(teamID keybase1.TeamID, public bool) libkb.DbKey {
    39  	key := genericStringKey(teamID, public)
    40  	dbKey := libkb.DbKey{
    41  		Typ: libkb.DBTeamMerkleCheck,
    42  		Key: key,
    43  	}
    44  	return dbKey
    45  }
    46  
    47  func (s *Merkle) Put(mctx libkb.MetaContext, teamID keybase1.TeamID, public bool, time keybase1.Time) {
    48  	s.Lock()
    49  	defer s.Unlock()
    50  	key := merkleKey(teamID, public)
    51  	s.lru.Add(key.Key, time)
    52  	obj := merkleDiskStorageItem{
    53  		Version:  merkleDiskStorageVersion,
    54  		PolledAt: time,
    55  	}
    56  	mctx.VLogf(libkb.VLog0, "teams/storage.Merkle#Put(%s) <- %d", teamID, time)
    57  	err := mctx.G().LocalDb.PutObj(key, nil, obj)
    58  	if err != nil {
    59  		mctx.Warning("teams/storage.Merkle: Failed to put key %+v: %s", key, err.Error())
    60  	}
    61  }
    62  
    63  func (s *Merkle) Get(mctx libkb.MetaContext, teamID keybase1.TeamID, public bool) (polledAt *keybase1.Time) {
    64  	s.Lock()
    65  	defer s.Unlock()
    66  	key := merkleKey(teamID, public)
    67  
    68  	report := func(res string, ret *keybase1.Time) {
    69  		var s string
    70  		if ret != nil {
    71  			s = fmt.Sprintf(" -> %d", *ret)
    72  		}
    73  		mctx.VLogf(libkb.VLog0, "teams/storage.Merkle#Get(%s) -> %s%s", teamID, res, s)
    74  	}
    75  
    76  	untyped, ok := s.lru.Get(key.Key)
    77  	if ok {
    78  		ret, ok := untyped.(keybase1.Time)
    79  		if ok {
    80  			report("hit mem", &ret)
    81  			return &ret
    82  		}
    83  		mctx.Warning("teams/storage.Merkle: Pulled object at %+v of wrong type: %T", key, untyped)
    84  	}
    85  	var tmp merkleDiskStorageItem
    86  	found, err := mctx.G().LocalDb.GetInto(&tmp, key)
    87  	if !found {
    88  		report("missed", nil)
    89  		return nil
    90  	}
    91  	if err != nil {
    92  		mctx.Warning("teams/storage.Merkle: error fetching %+v from disk: %s", key, err.Error())
    93  	}
    94  	if tmp.Version != merkleDiskStorageVersion {
    95  		mctx.Debug("teams/storage.Merkle: skipping old version %d for key %+v", tmp.Version, key)
    96  		return nil
    97  	}
    98  	report("hit disk", &tmp.PolledAt)
    99  	return &tmp.PolledAt
   100  }