diff --git a/.github/workflows/test-coherence.yml b/.github/workflows/test-coherence.yml index 6008a340..1e1a6b9f 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:23.09.2 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 && COHERENCE_SESSION_DEBUG=true 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..36d51462 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 } @@ -143,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 009fecd2..db599b3a 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(4) * time.Second, }) code := m.Run() @@ -102,7 +103,6 @@ func Test_Coherence_Set_With_Expiry(t *testing.T) { val, err = testStore.Get(key1) require.NoError(t, err) require.True(t, len(val) == 0) - } func Test_Coherence_Get_Missing(t *testing.T) {