forked from orijtech/groupcache
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #41 from vimeo/proto_fetcher_wrapper
protocodec: add BackendGetterV2 and GalaxyGet
- Loading branch information
Showing
3 changed files
with
174 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
//go:build go1.18 | ||
|
||
package protocodec | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/vimeo/galaxycache" | ||
|
||
"google.golang.org/protobuf/proto" | ||
) | ||
|
||
// BackendGetterV2 is an adapter that implements galaxycache.BackendGetter | ||
// (it wraps an unexported type because type-inference is much better on function-calls) | ||
func BackendGetterV2[C any, T pointerMessage[C]](f func(ctx context.Context, key string) (T, error)) galaxycache.BackendGetter { | ||
return backendGetterV2[C, T](f) | ||
} | ||
|
||
// backendGetterV2 is an adapter type that implements galaxycache.BackendGetter | ||
type backendGetterV2[C any, T pointerMessage[C]] func(ctx context.Context, key string) (T, error) | ||
|
||
// Get populates dest with the value identified by key | ||
// The returned data must be unversioned. That is, key must | ||
// uniquely describe the loaded data, without an implicit | ||
// current time, and without relying on cache expiration | ||
// mechanisms. | ||
func (b backendGetterV2[C, T]) Get(ctx context.Context, key string, dest galaxycache.Codec) error { | ||
out, bgErr := b(ctx, key) | ||
if bgErr != nil { | ||
return bgErr | ||
} | ||
switch d := dest.(type) { | ||
case *CodecV2[C, T]: | ||
d.Set(out) | ||
default: | ||
vs, mErr := proto.Marshal(out) | ||
if mErr != nil { | ||
return fmt.Errorf("failed to marshal value as bytes: %w", mErr) | ||
} | ||
|
||
if uErr := dest.UnmarshalBinary(vs); uErr != nil { | ||
return fmt.Errorf("destination codec (type %T) Unmarshal failed: %w", dest, uErr) | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
//go:build go1.18 | ||
|
||
package protocodec_test | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"testing" | ||
|
||
"google.golang.org/protobuf/proto" | ||
|
||
"github.com/vimeo/galaxycache" | ||
"github.com/vimeo/galaxycache/protocodec" | ||
"github.com/vimeo/galaxycache/protocodec/internal/testpbv2" | ||
) | ||
|
||
// Test of common-good-case | ||
func TestBackendGetterV2Good(t *testing.T) { | ||
beGood := func(ctx context.Context, key string) (*testpbv2.TestMessage, error) { | ||
return &testpbv2.TestMessage{ | ||
Name: proto.String("TestName"), | ||
City: proto.String("TestCity"), | ||
}, nil | ||
} | ||
|
||
be := protocodec.BackendGetterV2(beGood) | ||
|
||
ctx := context.Background() | ||
|
||
// test with a proto codec passed (local common-case) | ||
{ | ||
pc := protocodec.NewV2[testpbv2.TestMessage]() | ||
|
||
if getErr := be.Get(ctx, "foobar", &pc); getErr != nil { | ||
t.Errorf("noop Get call failed: %s", getErr) | ||
} | ||
|
||
pv := pc.Get() | ||
if pv.City == nil || *pv.City != "TestCity" { | ||
t.Errorf("unexpected value for City: %v", pv.City) | ||
} | ||
if pv.Name == nil || *pv.Name != "TestName" { | ||
t.Errorf("unexpected value for Name: %v", pv.Name) | ||
} | ||
} | ||
// test with a ByteCodec to exercise the common-case when a remote-fetch is done | ||
{ | ||
c := galaxycache.ByteCodec{} | ||
|
||
if getErr := be.Get(ctx, "foobar", &c); getErr != nil { | ||
t.Errorf("noop Get call failed: %s", getErr) | ||
} | ||
|
||
if len(c) < len("TestName")+len("TestCity") { | ||
t.Errorf("marshaled bytes too short (less than sum of two string fields)") | ||
} | ||
|
||
pc := protocodec.NewV2[testpbv2.TestMessage]() | ||
|
||
if umErr := pc.UnmarshalBinary([]byte(c)); umErr != nil { | ||
t.Errorf("failed to unmarshal bytes: %s", umErr) | ||
} | ||
|
||
pv := pc.Get() | ||
if pv.City == nil || *pv.City != "TestCity" { | ||
t.Errorf("unexpected value for City: %v", pv.City) | ||
} | ||
if pv.Name == nil || *pv.Name != "TestName" { | ||
t.Errorf("unexpected value for Name: %v", pv.Name) | ||
} | ||
} | ||
} | ||
|
||
func TestBackendGetterV2Bad(t *testing.T) { | ||
sentinel := errors.New("sentinel error") | ||
|
||
beErrorer := func(ctx context.Context, key string) (*testpbv2.TestMessage, error) { | ||
return nil, fmt.Errorf("error: %w", sentinel) | ||
} | ||
|
||
be := protocodec.BackendGetterV2(beErrorer) | ||
|
||
ctx := context.Background() | ||
|
||
// test with a proto codec passed (local common-case) | ||
{ | ||
pc := protocodec.NewV2[testpbv2.TestMessage]() | ||
|
||
if getErr := be.Get(ctx, "foobar", &pc); getErr == nil { | ||
t.Errorf("noop Get call didn't fail") | ||
} else if !errors.Is(getErr, sentinel) { | ||
t.Errorf("Error from Get did not wrap/equal sentinel") | ||
} | ||
} | ||
// test with a ByteCodec to exercise the common-case when a remote-fetch is done | ||
{ | ||
c := galaxycache.ByteCodec{} | ||
|
||
if getErr := be.Get(ctx, "foobar", &c); getErr == nil { | ||
t.Errorf("noop Get call didn't fail") | ||
} else if !errors.Is(getErr, sentinel) { | ||
t.Errorf("Error from Get did not wrap/equal sentinel") | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
//go:build go1.18 | ||
|
||
package protocodec | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/vimeo/galaxycache" | ||
) | ||
|
||
// GalaxyGet is a simple wrapper around a Galaxy.Get method-call that takes | ||
// care of constructing the protocodec.CodecV2, etc. (making the interface more idomatic for Go) | ||
func GalaxyGet[C any, T pointerMessage[C]](ctx context.Context, g *galaxycache.Galaxy, key string) (T, error) { | ||
pc := NewV2[C, T]() | ||
getErr := g.Get(ctx, key, &pc) | ||
if getErr != nil { | ||
return nil, getErr | ||
} | ||
return pc.Get(), nil | ||
} |