124 lines
2.5 KiB
Go
124 lines
2.5 KiB
Go
package etcd
|
|
|
|
import (
|
|
"encoding/json"
|
|
|
|
storepkg "github.com/cirruslabs/orchard/internal/controller/store"
|
|
clientv3 "go.etcd.io/etcd/client/v3"
|
|
)
|
|
|
|
func genericSet[T any](txn *Transaction, key string, obj T) error {
|
|
valueBytes, err := json.Marshal(obj)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
txn.puts[key] = string(valueBytes)
|
|
delete(txn.deletes, key)
|
|
|
|
return nil
|
|
}
|
|
|
|
func genericGet[T any, PT interface {
|
|
SetVersion(uint64)
|
|
*T
|
|
}](txn *Transaction, key string) (*T, error) {
|
|
if value, ok := txn.puts[key]; ok {
|
|
var obj T
|
|
if err := json.Unmarshal([]byte(value), &obj); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &obj, nil
|
|
}
|
|
if txn.isDeleted(key) {
|
|
return nil, storepkg.ErrNotFound
|
|
}
|
|
|
|
response, err := txn.store.client.Get(txn.ctx, key)
|
|
if err != nil {
|
|
return nil, mapErr(err)
|
|
}
|
|
if len(response.Kvs) == 0 {
|
|
txn.readRevisions[key] = 0
|
|
|
|
return nil, storepkg.ErrNotFound
|
|
}
|
|
|
|
kv := response.Kvs[0]
|
|
txn.readRevisions[key] = kv.ModRevision
|
|
|
|
var obj T
|
|
if err := json.Unmarshal(kv.Value, &obj); err != nil {
|
|
return nil, err
|
|
}
|
|
PT(&obj).SetVersion(uint64(kv.ModRevision))
|
|
|
|
return &obj, nil
|
|
}
|
|
|
|
func genericList[T any, PT interface {
|
|
SetVersion(uint64)
|
|
*T
|
|
}](txn *Transaction, logicalPrefix string) ([]T, error) {
|
|
physicalPrefix := txn.store.keyPrefix(logicalPrefix)
|
|
response, err := txn.store.client.Get(txn.ctx, physicalPrefix, clientv3.WithPrefix())
|
|
if err != nil {
|
|
return nil, mapErr(err)
|
|
}
|
|
txn.prefixReadRevisions[physicalPrefix] = response.Header.Revision
|
|
|
|
itemsByKey := map[string]T{}
|
|
keys := make([]string, 0, len(response.Kvs)+len(txn.puts))
|
|
|
|
for _, kv := range response.Kvs {
|
|
var obj T
|
|
if err := json.Unmarshal(kv.Value, &obj); err != nil {
|
|
return nil, err
|
|
}
|
|
PT(&obj).SetVersion(uint64(kv.ModRevision))
|
|
|
|
key := string(kv.Key)
|
|
itemsByKey[key] = obj
|
|
keys = append(keys, key)
|
|
}
|
|
|
|
for key, value := range txn.puts {
|
|
if !hasPrefix(key, physicalPrefix) {
|
|
continue
|
|
}
|
|
|
|
var obj T
|
|
if err := json.Unmarshal([]byte(value), &obj); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if _, ok := itemsByKey[key]; !ok {
|
|
keys = append(keys, key)
|
|
}
|
|
itemsByKey[key] = obj
|
|
}
|
|
|
|
result := []T{}
|
|
for _, key := range keys {
|
|
if txn.isDeleted(key) {
|
|
continue
|
|
}
|
|
|
|
result = append(result, itemsByKey[key])
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func genericDelete(txn *Transaction, key string) error {
|
|
delete(txn.puts, key)
|
|
txn.deletes[key] = struct{}{}
|
|
|
|
return nil
|
|
}
|
|
|
|
func hasPrefix(value, prefix string) bool {
|
|
return len(value) >= len(prefix) && value[:len(prefix)] == prefix
|
|
}
|