-
Notifications
You must be signed in to change notification settings - Fork 8
/
kvstore.go
131 lines (114 loc) · 3.31 KB
/
kvstore.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Package kvstore provides access to Fastly KV stores.
//
// KV stores provide durable storage of key/value data that is readable
// and writable at the edge and synchronized globally.
//
// See the [Fastly KV store documentation] for details.
//
// [Fastly KV store documentation]: https://developer.fastly.com/learning/concepts/data-stores/#kv-stores
package kvstore
import (
"errors"
"fmt"
"io"
"github.com/fastly/compute-sdk-go/internal/abi/fastly"
)
var (
// ErrStoreNotFound indicates that the named store doesn't exist.
ErrStoreNotFound = errors.New("kvstore: store not found")
// ErrKeyNotFound indicates that the named key doesn't exist in this
// KV store.
ErrKeyNotFound = errors.New("kvstore: key not found")
// ErrInvalidKey indicates that the given key is invalid.
ErrInvalidKey = errors.New("kvstore: invalid key")
// ErrUnexpected indicates than an unexpected error occurred.
ErrUnexpected = errors.New("kvstore: unexpected error")
)
// Entry represents a KV store value.
//
// It embeds an [io.Reader] which holds the contents of the value, and
// can be passed to functions that accept an [io.Reader].
//
// For smaller values, an [Entry.String] method is provided to consume the
// contents of the underlying reader and return a string.
//
// Do not mix-and-match these approaches: use either the [io.Reader] or
// the [Entry.String] method, not both.
type Entry struct {
io.Reader
validString bool
s string
}
// String consumes the entire contents of the Entry and returns it as a
// string.
//
// Take care when using this method, as large values might exceed the
// per-request memory limit.
func (e *Entry) String() string {
if e.validString {
return e.s
}
// TODO(dgryski): replace with StringBuilder + io.Copy ?
b, err := io.ReadAll(e)
if err != nil {
return ""
}
e.s = string(b)
e.validString = true
return e.s
}
// Store represents a Fastly KV store
type Store struct {
kvstore *fastly.KVStore
}
// Open returns a handle to the named kv store
func Open(name string) (*Store, error) {
o, err := fastly.OpenKVStore(name)
if err != nil {
status, ok := fastly.IsFastlyError(err)
switch {
case ok && status == fastly.FastlyStatusInval:
return nil, ErrStoreNotFound
case ok:
return nil, fmt.Errorf("%w (%s)", ErrUnexpected, status)
default:
return nil, err
}
}
return &Store{kvstore: o}, nil
}
// Lookup fetches a key from the associated KV store. If the key does not
// exist, Lookup returns the sentinel error [ErrKeyNotFound].
func (s *Store) Lookup(key string) (*Entry, error) {
val, err := s.kvstore.Lookup(key)
if err != nil {
status, ok := fastly.IsFastlyError(err)
switch {
case ok && status == fastly.FastlyStatusNone:
return nil, ErrKeyNotFound
case ok && status == fastly.FastlyStatusInval:
return nil, ErrInvalidKey
case ok:
return nil, fmt.Errorf("%w (%s)", ErrUnexpected, status)
default:
return nil, err
}
}
return &Entry{Reader: val}, err
}
// Insert adds a key to the associated KV store.
func (s *Store) Insert(key string, value io.Reader) error {
err := s.kvstore.Insert(key, value)
if err != nil {
status, ok := fastly.IsFastlyError(err)
switch {
case ok && status == fastly.FastlyStatusInval:
return ErrInvalidKey
case ok:
return fmt.Errorf("%w (%s)", ErrUnexpected, status)
default:
return err
}
}
return nil
}