Skip to content

Commit d5ffbc5

Browse files
authored
Adds Default TTL Option for Redis State Store (dapr#1059)
* Adds Default TTL Option for Redis State Store * Add additional tests * Parse correct property key Co-authored-by: Bernd Verst <me@bernd.dev>
1 parent 7551e98 commit d5ffbc5

File tree

3 files changed

+116
-0
lines changed

3 files changed

+116
-0
lines changed

state/redis/metadata.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ import "time"
1010
type metadata struct {
1111
maxRetries int
1212
maxRetryBackoff time.Duration
13+
ttlInSeconds *int
1314
}

state/redis/redis.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,17 @@ func parseRedisMetadata(meta state.Metadata) (metadata, error) {
8787
m.maxRetryBackoff = time.Duration(parsedVal)
8888
}
8989

90+
if val, ok := meta.Properties[ttlInSeconds]; ok && val != "" {
91+
parsedVal, err := strconv.ParseInt(val, defaultBase, defaultBitSize)
92+
if err != nil {
93+
return m, fmt.Errorf("redis store error: can't parse ttlInSeconds field: %s", err)
94+
}
95+
intVal := int(parsedVal)
96+
m.ttlInSeconds = &intVal
97+
} else {
98+
m.ttlInSeconds = nil
99+
}
100+
90101
return m, nil
91102
}
92103

@@ -235,6 +246,10 @@ func (r *StateStore) setValue(req *state.SetRequest) error {
235246
if err != nil {
236247
return fmt.Errorf("failed to parse ttl from metadata: %s", err)
237248
}
249+
// apply global TTL
250+
if ttl == nil {
251+
ttl = r.metadata.ttlInSeconds
252+
}
238253

239254
bt, _ := utils.Marshal(req.Value, r.json.Marshal)
240255

@@ -295,6 +310,11 @@ func (r *StateStore) Multi(request *state.TransactionalStateRequest) error {
295310
if err != nil {
296311
return fmt.Errorf("failed to parse ttl from metadata: %s", err)
297312
}
313+
// apply global TTL
314+
if ttl == nil {
315+
ttl = r.metadata.ttlInSeconds
316+
}
317+
298318
bt, _ := utils.Marshal(req.Value, r.json.Marshal)
299319
pipe.Do(r.ctx, "EVAL", setQuery, 1, req.Key, ver, bt)
300320
if ttl != nil && *ttl > 0 {

state/redis/redis_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,101 @@ func TestPing(t *testing.T) {
308308
assert.Error(t, err)
309309
}
310310

311+
func TestRequestsWithGlobalTTL(t *testing.T) {
312+
s, c := setupMiniredis()
313+
defer s.Close()
314+
315+
globalTTLInSeconds := 100
316+
317+
ss := &StateStore{
318+
client: c,
319+
json: jsoniter.ConfigFastest,
320+
logger: logger.NewLogger("test"),
321+
metadata: metadata{ttlInSeconds: &globalTTLInSeconds},
322+
}
323+
ss.ctx, ss.cancel = context.WithCancel(context.Background())
324+
325+
t.Run("TTL: Only global specified", func(t *testing.T) {
326+
ss.Set(&state.SetRequest{
327+
Key: "weapon100",
328+
Value: "deathstar100",
329+
})
330+
ttl, _ := ss.client.TTL(ss.ctx, "weapon100").Result()
331+
332+
assert.Equal(t, time.Duration(globalTTLInSeconds)*time.Second, ttl)
333+
})
334+
335+
t.Run("TTL: Global and Request specified", func(t *testing.T) {
336+
requestTTL := 200
337+
ss.Set(&state.SetRequest{
338+
Key: "weapon100",
339+
Value: "deathstar100",
340+
Metadata: map[string]string{
341+
"ttlInSeconds": strconv.Itoa(requestTTL),
342+
},
343+
})
344+
ttl, _ := ss.client.TTL(ss.ctx, "weapon100").Result()
345+
346+
assert.Equal(t, time.Duration(requestTTL)*time.Second, ttl)
347+
})
348+
349+
t.Run("TTL: Global and Request specified", func(t *testing.T) {
350+
err := ss.Multi(&state.TransactionalStateRequest{
351+
Operations: []state.TransactionalStateOperation{
352+
{
353+
Operation: state.Upsert,
354+
Request: state.SetRequest{
355+
Key: "weapon",
356+
Value: "deathstar",
357+
},
358+
},
359+
{
360+
Operation: state.Upsert,
361+
Request: state.SetRequest{
362+
Key: "weapon2",
363+
Value: "deathstar2",
364+
Metadata: map[string]string{
365+
"ttlInSeconds": "123",
366+
},
367+
},
368+
},
369+
{
370+
Operation: state.Upsert,
371+
Request: state.SetRequest{
372+
Key: "weapon3",
373+
Value: "deathstar3",
374+
Metadata: map[string]string{
375+
"ttlInSeconds": "-1",
376+
},
377+
},
378+
},
379+
},
380+
})
381+
assert.Equal(t, nil, err)
382+
383+
res, err := c.Do(context.Background(), "HGETALL", "weapon").Result()
384+
assert.Equal(t, nil, err)
385+
386+
vals := res.([]interface{})
387+
data, version, err := ss.getKeyVersion(vals)
388+
assert.Equal(t, nil, err)
389+
assert.Equal(t, ptr.String("1"), version)
390+
assert.Equal(t, `"deathstar"`, data)
391+
392+
res, err = c.Do(context.Background(), "TTL", "weapon").Result()
393+
assert.Equal(t, nil, err)
394+
assert.Equal(t, int64(globalTTLInSeconds), res)
395+
396+
res, err = c.Do(context.Background(), "TTL", "weapon2").Result()
397+
assert.Equal(t, nil, err)
398+
assert.Equal(t, int64(123), res)
399+
400+
res, err = c.Do(context.Background(), "TTL", "weapon3").Result()
401+
assert.Equal(t, nil, err)
402+
assert.Equal(t, int64(-1), res)
403+
})
404+
}
405+
311406
func TestSetRequestWithTTL(t *testing.T) {
312407
s, c := setupMiniredis()
313408
defer s.Close()

0 commit comments

Comments
 (0)