From 21a2ad89f2ceef20e17a7dc4a8facd83de433642 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Fri, 8 Mar 2024 15:39:04 +0800 Subject: [PATCH 1/5] Update Coherence driver to add near cache option --- .github/workflows/test-coherence.yml | 5 ++-- coherence/README.md | 40 +++++++++++++++++++++++++--- coherence/coherence.go | 37 ++++++++++++++++++++----- coherence/coherence_test.go | 8 +++--- coherence/go.mod | 2 +- coherence/go.sum | 4 +-- 6 files changed, 77 insertions(+), 19 deletions(-) diff --git a/.github/workflows/test-coherence.yml b/.github/workflows/test-coherence.yml index 6008a340..2053d1f1 100644 --- a/.github/workflows/test-coherence.yml +++ b/.github/workflows/test-coherence.yml @@ -18,16 +18,17 @@ jobs: - 1.19.x - 1.20.x - 1.21.x + - 1.22.x steps: - name: Fetch Repository uses: actions/checkout@v4 - name: Startup Coherence run: | - docker run -d -p 1408:1408 -p 30000:30000 ghcr.io/oracle/coherence-ce:22.06.5 + docker run -d -p 1408:1408 -p 30000:30000 ghcr.io/oracle/coherence-ce:22.06.7 sleep 30 - name: Install Go uses: actions/setup-go@v5 with: go-version: '${{ matrix.go-version }}' - name: Run Test - run: cd ./coherence && go clean -testcache && go test ./... -v + run: cd ./coherence && go clean -testcache && go test ./... -v -race diff --git a/coherence/README.md b/coherence/README.md index 2fe828c6..f4d12aae 100644 --- a/coherence/README.md +++ b/coherence/README.md @@ -35,7 +35,7 @@ necessary for the client to operate correctly. To start a Coherence cluster using Docker, issue the following: ```bash -docker run -d -p 1408:1408 ghcr.io/oracle/coherence-ce:22.06.5 +docker run -d -p 1408:1408 ghcr.io/oracle/coherence-ce:22.06.7 ``` See the documentation [here](https://pkg.go.dev/github.com/oracle/coherence-go-client/coherence#hdr-Obtaining_a_Session) on connection options @@ -52,9 +52,10 @@ You can use the following possibilities to create a storage: // Initialize default config, to connect to localhost:1408 using plain text store, err := coherence.New() -// Initialize custom config to connect to a different host/port and use plaint ext. +// Initialize custom config to connect to a different host/port and use plain text and expiry of 5 minutes. store, err := coherence.New(coherence.Config{ Address: "my-host:myport", + Expiration: time.Duration(300) * time.Second, // 5 minutes }) // Initialize to connect with TLS enabled with your own tls.Config @@ -66,13 +67,38 @@ store, err := coherence.New(coherence.Config{ }) ``` -> Note: If you create two stores using `coherence.New()` they will effectivity be idential. +> Note: If you create two stores using `coherence.New()` they will effectivity be identical. > If you wish to have two separate stores, then you can use: > ```go > store1, err := coherence.New(Config{ScopeName: "scope1"}) > store2, err := coherence.New(Config{ScopeName: "scope2"}) > ``` +**Near Caches** + +The latest version of the Coherence Go client introduces near cache support +to cache frequently accessed data in the Go client to avoid sending requests across the network. + +This is particularly useful if you are using sticky sessions via a LBR as this will cache +the session in the Go process and the `Get()` operations will be much quicker. + +When the session is expired on the server it will automatically be removed from the near cache. + +To enable this for you session, you can set the `NearCacheTimeout` to a duration less than the expiry. + +```go +// Initialize default config, to connect to localhost:1408 using plain text +store, err := coherence.New() + +// Use plain text with default expiry of 5 minutes, and a near cache expiry of 2 minutes +store, err := coherence.New(coherence.Config{ + Address: "my-host:myport", + Expiration: time.Duration(300) * time.Second, // 5 minutes + NearCacheTimeout: time.Duration(120) * time.Second, // 2 minutes +}) +``` +> Note: You must ensure your near cache timeout is less that the session timeout. + ### Config ```go @@ -92,6 +118,11 @@ type Config struct { // TLSConfig specifies tls.Config to use when connecting, if nil then plain text is used TLSConfig *tls.Config + + // NearCacheTimeout defines the timeout for a near cache. Is this is set, then a near cache + // with the timeout is created. Note: this must be less than the session timeout or any timeout you specify + // when using Set(). + NearCacheTimeout time.Duration } ``` @@ -99,8 +130,9 @@ type Config struct { ```go var DefaultConfig = Config{ Address: "localhost:1408", - Timeout: time.Duration(30) * time.Millisecond, + Timeout: time.Duration(120) * time.Seconds, ScopeName: defaultScopeName, Reset: false, + NearCacheTimeout: time.Duration(60) * time.Seconds, } ``` diff --git a/coherence/coherence.go b/coherence/coherence.go index 0fab56e4..6c4fa328 100644 --- a/coherence/coherence.go +++ b/coherence/coherence.go @@ -1,11 +1,12 @@ package coherence /* - * Copyright © 2023, Oracle and/or its affiliates. + * Copyright © 2023, 2024 Oracle and/or its affiliates. */ import ( "context" "crypto/tls" + "fmt" coh "github.com/oracle/coherence-go-client/coherence" "time" ) @@ -39,6 +40,11 @@ type Config struct { // TLSConfig specifies tls.Config to use when connecting, if nil then plain text is used TLSConfig *tls.Config + + // NearCacheTimeout defines the timeout for a near cache. Is this is set, then a near cache + // with the timeout is created. Note: this must be less than the session timeout or any timeout you specify + // when using Set(). + NearCacheTimeout time.Duration } // DefaultConfig defines default options. @@ -51,9 +57,10 @@ var DefaultConfig = Config{ // New returns a new [Storage] given a [Config]. func New(config ...Config) (*Storage, error) { - cfg := setupConfig(config...) - - options := make([]func(session *coh.SessionOptions), 0) + var ( + cfg = setupConfig(config...) + options = make([]func(session *coh.SessionOptions), 0) + ) // apply any config values as Coherence options options = append(options, coh.WithAddress(cfg.Address)) @@ -66,13 +73,21 @@ func New(config ...Config) (*Storage, error) { options = append(options, coh.WithRequestTimeout(cfg.Timeout)) + // validate near cache options + if cfg.NearCacheTimeout != 0 { + if cfg.NearCacheTimeout > cfg.Timeout { + return nil, fmt.Errorf("you cannot set the near cache timeout (%v) to less than session timeout (%v)", + cfg.NearCacheTimeout, cfg.Timeout) + } + } + // create the Coherence session session, err := coh.NewSession(context.Background(), options...) if err != nil { return nil, err } - store, err := newCoherenceStorage(session, cfg.ScopeName) + store, err := newCoherenceStorage(session, cfg.ScopeName, cfg.NearCacheTimeout) if err != nil { return nil, err } @@ -109,8 +124,16 @@ func setupConfig(config ...Config) Config { } // newCoherenceStorage returns a new Coherence [Storage]. -func newCoherenceStorage(session *coh.Session, cacheName string) (*Storage, error) { - nc, err := coh.GetNamedCache[string, []byte](session, "fiber$"+cacheName) +func newCoherenceStorage(session *coh.Session, cacheName string, nearCacheTimeout time.Duration) (*Storage, error) { + cacheOptions := make([]func(cache *coh.CacheOptions), 0) + + // configure a near cache if the nearCacheTimeout is set + if nearCacheTimeout != 0 { + nearCacheOptions := coh.NearCacheOptions{TTL: nearCacheTimeout} + cacheOptions = append(cacheOptions, coh.WithNearCache(&nearCacheOptions)) + } + + nc, err := coh.GetNamedCache[string, []byte](session, "fiber$"+cacheName, cacheOptions...) if err != nil { return nil, err } diff --git a/coherence/coherence_test.go b/coherence/coherence_test.go index 009fecd2..b385d3c0 100644 --- a/coherence/coherence_test.go +++ b/coherence/coherence_test.go @@ -1,7 +1,7 @@ package coherence /* - * Copyright © 2023, Oracle and/or its affiliates. + * Copyright © 2023, 2024 Oracle and/or its affiliates. */ import ( "github.com/stretchr/testify/require" @@ -22,7 +22,8 @@ var testStore *Storage func TestMain(m *testing.M) { testStore, _ = New(Config{ - Reset: true, + Reset: true, + NearCacheTimeout: time.Duration(20) * time.Second, }) code := m.Run() @@ -34,8 +35,9 @@ func TestMain(m *testing.M) { // newTestStore returns a new Coherence Store and ensures it is reset. func newTestStore(t testing.TB, config ...Config) (*Storage, error) { t.Helper() + var err error - testStore, err := New(config...) + testStore, err = New(config...) require.NoError(t, err) err = testStore.Reset() diff --git a/coherence/go.mod b/coherence/go.mod index 78d9f2da..99a928ab 100644 --- a/coherence/go.mod +++ b/coherence/go.mod @@ -3,7 +3,7 @@ module github.com/gofiber/storage/coherence go 1.19 require ( - github.com/oracle/coherence-go-client v1.0.3 + github.com/oracle/coherence-go-client v1.1.0 github.com/stretchr/testify v1.9.0 ) diff --git a/coherence/go.sum b/coherence/go.sum index 0ab38ec5..3acdb4f3 100644 --- a/coherence/go.sum +++ b/coherence/go.sum @@ -12,8 +12,8 @@ github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= -github.com/oracle/coherence-go-client v1.0.3 h1:P8Rzgo21BAaJsKzemzMCFY9I27PdKBpr5ZqrHhZ7zPg= -github.com/oracle/coherence-go-client v1.0.3/go.mod h1:IAk8etsxzhUK6YaGzbInR1LYlh+1fiG85bGpyvzY0QY= +github.com/oracle/coherence-go-client v1.1.0 h1:Hra1R6xcszzHfTbhzsfLkI5xn2/kxoLBHagvE+2jiUU= +github.com/oracle/coherence-go-client v1.1.0/go.mod h1:IAk8etsxzhUK6YaGzbInR1LYlh+1fiG85bGpyvzY0QY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= From 1bcb639dd60dca8f8b09206c8e6a5431a38c5b72 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Fri, 8 Mar 2024 15:48:58 +0800 Subject: [PATCH 2/5] Fix test errors --- coherence/coherence_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/coherence/coherence_test.go b/coherence/coherence_test.go index b385d3c0..96cfeb97 100644 --- a/coherence/coherence_test.go +++ b/coherence/coherence_test.go @@ -22,8 +22,7 @@ var testStore *Storage func TestMain(m *testing.M) { testStore, _ = New(Config{ - Reset: true, - NearCacheTimeout: time.Duration(20) * time.Second, + Reset: true, }) code := m.Run() @@ -99,12 +98,11 @@ func Test_Coherence_Set_With_Expiry(t *testing.T) { // set with an expiry of 5 seconds err := testStore.Set(key1, value1, time.Duration(5)*time.Second) require.NoError(t, err) - time.Sleep(time.Duration(6) * time.Second) + time.Sleep(time.Duration(10) * time.Second) val, err = testStore.Get(key1) require.NoError(t, err) require.True(t, len(val) == 0) - } func Test_Coherence_Get_Missing(t *testing.T) { From b4e65c7daef644ca9620e272d7fca7add4753265 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Fri, 8 Mar 2024 15:52:02 +0800 Subject: [PATCH 3/5] Fix test errors --- .github/workflows/test-coherence.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-coherence.yml b/.github/workflows/test-coherence.yml index 2053d1f1..158a1173 100644 --- a/.github/workflows/test-coherence.yml +++ b/.github/workflows/test-coherence.yml @@ -24,11 +24,11 @@ jobs: uses: actions/checkout@v4 - name: Startup Coherence run: | - docker run -d -p 1408:1408 -p 30000:30000 ghcr.io/oracle/coherence-ce:22.06.7 + docker run -d -p 1408:1408 -p 30000:30000 ghcr.io/oracle/coherence-ce:22.06.5 sleep 30 - name: Install Go uses: actions/setup-go@v5 with: go-version: '${{ matrix.go-version }}' - name: Run Test - run: cd ./coherence && go clean -testcache && go test ./... -v -race + run: cd ./coherence && COHERENCE_SESSION_DEBUG=true go clean -testcache && go test ./... -v -race From 7c2d6950a01c4496c7a89e05ebfe2ec1e172bb18 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Fri, 8 Mar 2024 16:25:34 +0800 Subject: [PATCH 4/5] Fix failing test --- .github/workflows/test-coherence.yml | 2 +- coherence/coherence.go | 2 +- coherence/coherence_test.go | 10 ++++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test-coherence.yml b/.github/workflows/test-coherence.yml index 158a1173..1e1a6b9f 100644 --- a/.github/workflows/test-coherence.yml +++ b/.github/workflows/test-coherence.yml @@ -24,7 +24,7 @@ jobs: uses: actions/checkout@v4 - name: Startup Coherence run: | - docker run -d -p 1408:1408 -p 30000:30000 ghcr.io/oracle/coherence-ce:22.06.5 + docker run -d -p 1408:1408 -p 30000:30000 ghcr.io/oracle/coherence-ce:23.09.2 sleep 30 - name: Install Go uses: actions/setup-go@v5 diff --git a/coherence/coherence.go b/coherence/coherence.go index 6c4fa328..36d51462 100644 --- a/coherence/coherence.go +++ b/coherence/coherence.go @@ -166,7 +166,7 @@ func (s *Storage) Delete(key string) error { } func (s *Storage) Reset() error { - return s.namedCache.Clear(s.ctx) + return s.namedCache.Truncate(s.ctx) } func (s *Storage) Close() error { diff --git a/coherence/coherence_test.go b/coherence/coherence_test.go index 96cfeb97..5c36b561 100644 --- a/coherence/coherence_test.go +++ b/coherence/coherence_test.go @@ -4,6 +4,7 @@ package coherence * Copyright © 2023, 2024 Oracle and/or its affiliates. */ import ( + "fmt" "github.com/stretchr/testify/require" "os" "testing" @@ -22,7 +23,8 @@ var testStore *Storage func TestMain(m *testing.M) { testStore, _ = New(Config{ - Reset: true, + Reset: true, + NearCacheTimeout: time.Duration(4) * time.Second, }) code := m.Run() @@ -34,9 +36,8 @@ func TestMain(m *testing.M) { // newTestStore returns a new Coherence Store and ensures it is reset. func newTestStore(t testing.TB, config ...Config) (*Storage, error) { t.Helper() - var err error - testStore, err = New(config...) + testStore, err := New(config...) require.NoError(t, err) err = testStore.Reset() @@ -98,7 +99,7 @@ func Test_Coherence_Set_With_Expiry(t *testing.T) { // set with an expiry of 5 seconds err := testStore.Set(key1, value1, time.Duration(5)*time.Second) require.NoError(t, err) - time.Sleep(time.Duration(10) * time.Second) + time.Sleep(time.Duration(6) * time.Second) val, err = testStore.Get(key1) require.NoError(t, err) @@ -137,6 +138,7 @@ func Test_Coherence_Reset(t *testing.T) { // check the keys have expired val, err = testStore.Get(key1) require.NoError(t, err) + fmt.Println("val=", string(val)) require.True(t, len(val) == 0) val, err = testStore.Get(key2) From 1761a1900a7b81612ba54a22c87ce950c7a74291 Mon Sep 17 00:00:00 2001 From: Tim Middleton Date: Fri, 8 Mar 2024 16:34:23 +0800 Subject: [PATCH 5/5] minor --- coherence/coherence_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/coherence/coherence_test.go b/coherence/coherence_test.go index 5c36b561..db599b3a 100644 --- a/coherence/coherence_test.go +++ b/coherence/coherence_test.go @@ -4,7 +4,6 @@ package coherence * Copyright © 2023, 2024 Oracle and/or its affiliates. */ import ( - "fmt" "github.com/stretchr/testify/require" "os" "testing" @@ -138,7 +137,6 @@ func Test_Coherence_Reset(t *testing.T) { // check the keys have expired val, err = testStore.Get(key1) require.NoError(t, err) - fmt.Println("val=", string(val)) require.True(t, len(val) == 0) val, err = testStore.Get(key2)