diff --git a/examples/key-value/README.md b/examples/key-value/README.md index 4075173..d3c63e9 100644 --- a/examples/key-value/README.md +++ b/examples/key-value/README.md @@ -11,7 +11,7 @@ spin up --build In another terminal window, you can interact with the Spin app: ```sh -curl localhost:3000/hello +curl localhost:3000 ``` You should receive the following output: diff --git a/examples/key-value/main.go b/examples/key-value/main.go index 9ac000e..60e1515 100644 --- a/examples/key-value/main.go +++ b/examples/key-value/main.go @@ -34,10 +34,13 @@ func init() { return } - keys, err := store.GetKeys() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return + var keys []string + for key, err := range store.GetKeys() { + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + keys = append(keys, key) } w.Header().Set("Content-Type", "application/json") diff --git a/examples/key-value/spin.toml b/examples/key-value/spin.toml index f87042e4d..ffd2fa3 100644 --- a/examples/key-value/spin.toml +++ b/examples/key-value/spin.toml @@ -7,7 +7,7 @@ name = "hello-kv" version = "1.0.0" [[trigger.http]] -route = "/hello" +route = "/..." component = "hello" [component.hello] diff --git a/kv/kv.go b/kv/kv.go index 6bebc3f..55f44e2 100644 --- a/kv/kv.go +++ b/kv/kv.go @@ -3,8 +3,9 @@ package kv import ( "fmt" + "iter" - keyvalue "github.com/spinframework/spin-go-sdk/v3/imports/fermyon_spin_2_0_0_key_value" + keyvalue "github.com/spinframework/spin-go-sdk/v3/imports/spin_key_value_3_0_0_key_value" ) // Store represents a connection to a key-value store. @@ -76,14 +77,35 @@ func (s *Store) Exists(key string) (bool, error) { return result.Ok(), nil } -// GetKeys returns all the keys from the store. -func (s *Store) GetKeys() ([]string, error) { - result := s.store.GetKeys() - if result.IsErr() { - return nil, errorVariantToError(result.Err()) +// GetKeys returns an iterator over the keys in the store. Keys are yielded as +// they arrive from the host, allowing the consumer to process them +// concurrently with the underlying stream read. +// +// The iterator yields each key with a nil error. If the host reports an error +// after the stream completes, a final pair of ("", err) is yielded. Stopping +// the iteration early releases the underlying stream. +func (s *Store) GetKeys() iter.Seq2[string, error] { + return func(yield func(string, error) bool) { + stream, future := s.store.GetKeys() + defer stream.Drop() + + buf := make([]string, 64) + for { + n := stream.Read(buf) + for _, k := range buf[:n] { + if !yield(k, nil) { + return + } + } + if stream.WriterDropped() { + break + } + } + + if result := future.Read(); result.IsErr() { + yield("", errorVariantToError(result.Err())) + } } - - return result.Ok(), nil } func errorVariantToError(code keyvalue.Error) error { diff --git a/testdata/key-value/main.go b/testdata/key-value/main.go index 9ac000e..60e1515 100644 --- a/testdata/key-value/main.go +++ b/testdata/key-value/main.go @@ -34,10 +34,13 @@ func init() { return } - keys, err := store.GetKeys() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return + var keys []string + for key, err := range store.GetKeys() { + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + keys = append(keys, key) } w.Header().Set("Content-Type", "application/json")