Skip to content

Commit

Permalink
memached: if tllInSeconds larger than 30 days, convert to unix timest…
Browse files Browse the repository at this point in the history
…amp (#2892)

Signed-off-by: joshvanl <[email protected]>
  • Loading branch information
JoshVanL authored Jun 6, 2023
1 parent f27ece3 commit 85be43a
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 0 deletions.
10 changes: 10 additions & 0 deletions state/memcached/memcached.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/bradfitz/gomemcache/memcache"
jsoniter "github.com/json-iterator/go"
"k8s.io/utils/clock"

"github.com/dapr/components-contrib/metadata"
"github.com/dapr/components-contrib/state"
Expand All @@ -45,6 +46,7 @@ type Memcached struct {
client *memcache.Client
json jsoniter.API
logger logger.Logger
clock clock.Clock
}

type memcachedMetadata struct {
Expand All @@ -57,6 +59,7 @@ func NewMemCacheStateStore(logger logger.Logger) state.Store {
s := &Memcached{
json: jsoniter.ConfigFastest,
logger: logger,
clock: clock.RealClock{},
}
s.BulkStore = state.NewDefaultBulkStore(s)
return s
Expand Down Expand Up @@ -133,8 +136,15 @@ func (m *Memcached) parseTTL(req *state.SetRequest) (*int32, error) {
if err != nil {
return nil, err
}

parsedInt := int32(parsedVal)

// If ttl is more than 30 days, convert it to unix timestamp.
// https://github.com/memcached/memcached/wiki/Commands#standard-protocol
if parsedInt >= 60*60*24*30 {
parsedInt = int32(m.clock.Now().Unix()) + parsedInt
}

// Notice that for Dapr, -1 means "persist with no TTL".
// Memcached uses "0" as the non-expiring marker TTL.
// https://github.com/memcached/memcached/wiki/Commands#set
Expand Down
15 changes: 15 additions & 0 deletions state/memcached/memcached_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
clocktesting "k8s.io/utils/clock/testing"

"github.com/dapr/components-contrib/metadata"
"github.com/dapr/components-contrib/state"
Expand Down Expand Up @@ -85,6 +87,7 @@ func TestMemcachedMetadata(t *testing.T) {

func TestParseTTL(t *testing.T) {
store := NewMemCacheStateStore(logger.NewLogger("test")).(*Memcached)
store.clock = clocktesting.NewFakeClock(time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC))
t.Run("TTL Not an integer", func(t *testing.T) {
ttlInSeconds := "not an integer"
ttl, err := store.parseTTL(&state.SetRequest{
Expand Down Expand Up @@ -140,4 +143,16 @@ func TestParseTTL(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, int(*ttl), ttlInSeconds)
})

t.Run("TTL is Unix time larger than a month", func(t *testing.T) {
ttlInSeconds := 2 * 30 * 24 * 60 * 60
ttl, err := store.parseTTL(&state.SetRequest{
Metadata: map[string]string{
"ttlInSeconds": strconv.Itoa(ttlInSeconds),
},
})

require.NoError(t, err)
assert.Equal(t, time.Date(2023, 2, 30, 0, 0, 0, 0, time.UTC).Unix(), int64(*ttl))
})
}
28 changes: 28 additions & 0 deletions tests/certification/state/memcached/memcached_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,33 @@ func TestMemcached(t *testing.T) {
return nil
}

timeToLiveWithATwoMonthsTTL := func(ctx flow.Context) error {
client, err := client.NewClientWithPort(fmt.Sprint(currentGrpcPort))
if err != nil {
panic(err)
}
defer client.Close()

key := certificationTestPrefix + "_expiresInTwoMonthsKey"
value := "This key will self-destroy in 2 months"

ttlExpirationTime := 2 * 30 * 24 * time.Hour
ttlInSeconds := int(ttlExpirationTime.Seconds())
mapOptionsExpiringKey := map[string]string{
"ttlInSeconds": strconv.Itoa(ttlInSeconds),
}

errSave := client.SaveState(ctx, stateStoreName, key, []byte(value), mapOptionsExpiringKey)
assert.NoError(t, errSave)

// get state
item, errGetBeforeTTLExpiration := client.GetState(ctx, stateStoreName, key, nil)
assert.NoError(t, errGetBeforeTTLExpiration)
assert.Equal(t, value, string(item.Value))

return nil
}

flow.New(t, "Connecting Memcached And Test for CRUD operations").
Step(dockercompose.Run("memcached", dockerComposeClusterYAML)).
Step("Waiting for component to start...", flow.Sleep(5*time.Second)).
Expand Down Expand Up @@ -220,6 +247,7 @@ func TestMemcached(t *testing.T) {
Step("Run TTL related test: TTL not a valid number.", timeToLiveTestWithInvalidTTLValue).
Step("Run TTL related test: TTL not expiring.", timeToLiveTestWithNonExpiringTTL).
Step("Run TTL related test: TTL of 1 second.", timeToLiveWithAOneSecondTTL).
Step("Run TTL related test: TTL of 2 months.", timeToLiveWithATwoMonthsTTL).
Step("Stop Memcached server", dockercompose.Stop("memcached", dockerComposeClusterYAML)).
Run()
}
Expand Down

0 comments on commit 85be43a

Please sign in to comment.