From e8bdb6e5a7472ef0ff9a1cf86f3b2f83440b5d3c Mon Sep 17 00:00:00 2001 From: Doctor Vince Date: Mon, 26 Jun 2023 15:56:40 -0400 Subject: [PATCH] add a lookup for workers (#141) * add a lookup for workers * add a deserialize method for text id strings * LOL worked on my machine --- events/registry/registry.go | 19 +++++++++++++++++++ events/registry/registry_test.go | 9 +++++++++ events/registry/types.go | 22 ++++++++++++++++++++++ events/registry/types_test.go | 15 +++++++++++++++ 4 files changed, 65 insertions(+) diff --git a/events/registry/registry.go b/events/registry/registry.go index e726693..d2d58fb 100644 --- a/events/registry/registry.go +++ b/events/registry/registry.go @@ -28,6 +28,7 @@ var ( ErrRegistryUninitialized = errors.New("controller registry uninitialized") ErrRegistryPreviouslyInitialized = errors.New("controller registry previously initialized") + ErrBadRegistryData = errors.New("bad registry data") ) func InitializeActiveControllerRegistry(njs *events.NatsJetstream) error { @@ -91,3 +92,21 @@ func DeregisterController(id ControllerID) error { } return registry.Delete(id.String()) } + +func LastContact(id ControllerID) (time.Time, error) { + var zt time.Time + if registry == nil { + return zt, ErrRegistryUninitialized + } + entry, err := registry.Get(id.String()) + if err != nil { + return zt, err // this can either be a communication error or nats.ErrKeyNotFound + } + // if we have an entry the controller was alive in the last TTL period + var ar activityRecord + err = json.Unmarshal(entry.Value(), &ar) + if err != nil { + return zt, ErrBadRegistryData // consumers should *probably* treat this as a success? + } + return ar.LastActive, nil +} diff --git a/events/registry/registry_test.go b/events/registry/registry_test.go index 3074c86..7650f1e 100644 --- a/events/registry/registry_test.go +++ b/events/registry/registry_test.go @@ -4,6 +4,7 @@ package registry import ( "testing" + "github.com/nats-io/nats.go" "github.com/stretchr/testify/require" "go.hollow.sh/toolbox/events" @@ -24,6 +25,9 @@ func TestAppLifecycle(t *testing.T) { err = DeregisterController(id) require.Error(t, err) require.Equal(t, ErrRegistryUninitialized, err) + _, err = LastContact(id) + require.Error(t, err) + require.Equal(t, ErrRegistryUninitialized, err) //OK, now let's get serious srv := kvTest.StartJetStreamServer(t) @@ -43,6 +47,11 @@ func TestAppLifecycle(t *testing.T) { require.NoError(t, err) err = ControllerCheckin(id) require.NoError(t, err) + _, err = LastContact(id) + require.NoError(t, err) err = DeregisterController(id) require.NoError(t, err) + _, err = LastContact(id) + require.Error(t, err) + require.ErrorIs(t, err, nats.ErrKeyNotFound) } diff --git a/events/registry/types.go b/events/registry/types.go index d94e260..a36c64b 100644 --- a/events/registry/types.go +++ b/events/registry/types.go @@ -1,12 +1,19 @@ +// nolint: wsl // it's useless package registry import ( + "errors" "fmt" + "strings" "time" "github.com/google/uuid" ) +var ( + ErrBadFormat = errors.New("bad worker id format") +) + type ControllerID interface { fmt.Stringer updateVersion(uint64) @@ -23,6 +30,21 @@ func (id *workerUUID) String() string { return id.appName + "/" + id.uuid.String() } +func ControllerIDFromString(s string) (ControllerID, error) { + name, uuidStr, found := strings.Cut(s, "/") + if !found { + return nil, fmt.Errorf("%w: missing delimiter", ErrBadFormat) + } + uuid, err := uuid.Parse(uuidStr) + if err != nil { + return nil, fmt.Errorf("%w: %s", ErrBadFormat, err.Error()) + } + return &workerUUID{ + appName: name, + uuid: uuid, + }, nil +} + func (id *workerUUID) updateVersion(rev uint64) { id.kvVersion = rev } diff --git a/events/registry/types_test.go b/events/registry/types_test.go index fed42b6..8eb3257 100644 --- a/events/registry/types_test.go +++ b/events/registry/types_test.go @@ -1,8 +1,10 @@ +//nolint:all // it's a test package registry import ( "testing" + "github.com/google/uuid" "github.com/stretchr/testify/require" ) @@ -14,4 +16,17 @@ func TestWorkerID(t *testing.T) { id2 := GetID("myAppName") require.NotNil(t, id2) require.NotEqual(t, id.String(), id2.String()) + + idStr := id.String() + + reconstituted, err := ControllerIDFromString(idStr) + require.NoError(t, err) + require.Equal(t, id.(*workerUUID).appName, reconstituted.(*workerUUID).appName) + require.Equal(t, id.(*workerUUID).uuid, reconstituted.(*workerUUID).uuid) + + _, err = ControllerIDFromString(uuid.New().String()) + require.ErrorIs(t, err, ErrBadFormat, "no slash in name") + + _, err = ControllerIDFromString("app-name/bogus") + require.ErrorIs(t, err, ErrBadFormat, "bogus uuid") }