Skip to content

Commit

Permalink
Version 2.0.0 features and bugfixes
Browse files Browse the repository at this point in the history
- Make Context optional when creating a Map
  - Map.NewMap does not require a context parameter
    - Internally it uses context.Background
  - Map.NewMapContext does require a context parameter
    - A little easier on the user
- Add per-key-value-pair TTL
  - Rename Map.NewMap and Map.NewMapContext's maxTTL parameter to
    defaultTTL
    - Now just the default
    - key/value pairs inserted with Map.Store will use this default
  - Add Map.StoreWithTTL
    - Adds a custom TTL to each mapItem
- Fix bug in Store
  - If the item exists, the original value was not overwritten
- Rename "refreshOnStore" variables and params to "refreshOnLoad" which
  is actually what's going on
  • Loading branch information
glenvan committed Nov 12, 2023
1 parent c860820 commit 46e0b4f
Show file tree
Hide file tree
Showing 7 changed files with 278 additions and 136 deletions.
17 changes: 7 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ few enhancements and creature-comforts:
- Meaning that both key and value are determined by Generics
- Using `any` or `interface{}` as the value type will effectively emulate the original source
package
- `Map` accepts a [`context.Context`](https://pkg.go.dev/context@go1.21.4)
- `Map` can be created with a [`context.Context`](https://pkg.go.dev/context@go1.21.4)
- `Map` will automatically stop pruning expired items (equivalent to `Map.Close()`) if the
context cancels to prevent goroutine leaks
- Great for services
Expand All @@ -28,6 +28,8 @@ few enhancements and creature-comforts:
- The syntax is a little more idiomatic
- Methods have been renamed to be more familiar to Go standard library users
- `Load()` and `Store()` instead of `Get()` and `Set()`
- Key/value pairs can use the default TTL for the `Map`, or have their own individual TTL by
using `StoreWithTTL`
- Code is a little safer for concurrent use (at the time of the fork) and more performant in that
use case
- Use of `sync.RWLock` so that read-heavy applications block less
Expand Down Expand Up @@ -64,19 +66,14 @@ import (
)

func main() {
maxTTL := 300 * time.Millisecond // a key's time to live
defaultTTL := 300 * time.Millisecond // a key's time to live
startSize := 3 // initial number of items in map
pruneInterval := 100 * time.Millisecond // prune expired items each time pruneInterval elapses
refreshOnStore := true // update item's 'lastAccessTime' on ttl.Map.Load()
refreshOnLoad := true // update item's 'lastAccessTime' on ttl.Map.Load()

// Any comparable data type such as int, uint64, pointers and struct types (if all field
// types are comparable) can be used as the key type
t := ttl.NewMap[string, string](
context.Background(),
maxTTL,
startSize,
pruneInterval,
refreshOnStore)
t := ttl.NewMap[string, string](defaultTTL, startSize, pruneInterval, refreshOnLoad)
defer t.Close()

// Populate the ttl.Map
Expand All @@ -93,7 +90,7 @@ func main() {
return true
})

sleepTime := maxTTL + pruneInterval
sleepTime := defaultTTL + pruneInterval
fmt.Printf("Sleeping %s, items should be expired and removed afterward\n", sleepTime)

time.Sleep(sleepTime)
Expand Down
2 changes: 1 addition & 1 deletion Taskfile.dist.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ output: 'prefixed'

vars:
PKG_NAME: github.com/glenvan/ttl
PKG_VERSION: v1.0.0
PKG_VERSION: v2.0.0

tasks:
default:
Expand Down
15 changes: 5 additions & 10 deletions example/example.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
package main

import (
"context"
"fmt"
"time"

"github.com/glenvan/ttl"
)

func main() {
maxTTL := time.Duration(time.Second * 4) // time in seconds
defaultTTL := time.Duration(time.Second * 4) // time in seconds
startSize := 3 // initial number of items in map
pruneInterval := time.Duration(time.Second * 1) // search for expired items every 'pruneInterval' seconds
refreshOnStore := true // update item's lastAccessTime on a .Get()
t := ttl.NewMap[string, any](
context.Background(),
maxTTL,
startSize,
pruneInterval,
refreshOnStore)
refreshOnLoad := true // update item's lastAccessTime on a .Get()

t := ttl.NewMap[string, any](defaultTTL, startSize, pruneInterval, refreshOnLoad)
defer t.Close()

// populate the ttl.Map
Expand Down Expand Up @@ -54,7 +49,7 @@ func main() {
}()

// ttl.Map has an expiration time, wait until this amount of time passes
sleepTime := maxTTL + 2*pruneInterval
sleepTime := defaultTTL + 2*pruneInterval
fmt.Println()
fmt.Printf(
"Sleeping %v, items should be removed after this time, except for the '%v' key\n",
Expand Down
14 changes: 4 additions & 10 deletions example/small/small.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
package main

import (
"context"
"fmt"
"time"

"github.com/glenvan/ttl"
)

func main() {
maxTTL := 300 * time.Millisecond // a key's time to live
defaultTTL := 300 * time.Millisecond // a key's time to live
startSize := 3 // initial number of items in map
pruneInterval := 100 * time.Millisecond // prune expired items each time pruneInterval elapses
refreshOnStore := true // update item's 'lastAccessTime' on ttl.Map.Load()
refreshOnLoad := true // update item's 'lastAccessTime' on ttl.Map.Load()

// Any comparable data type such as int, uint64, pointers and struct types (if all field
// types are comparable) can be used as the key type
t := ttl.NewMap[string, string](
context.Background(),
maxTTL,
startSize,
pruneInterval,
refreshOnStore)
t := ttl.NewMap[string, string](defaultTTL, startSize, pruneInterval, refreshOnLoad)
defer t.Close()

// Populate the ttl.Map
Expand All @@ -38,7 +32,7 @@ func main() {
return true
})

sleepTime := maxTTL + pruneInterval
sleepTime := defaultTTL + pruneInterval
fmt.Printf("Sleeping %s, items should be expired and removed afterward\n", sleepTime)

time.Sleep(sleepTime)
Expand Down
21 changes: 8 additions & 13 deletions example_test.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
package ttl_test

import (
"context"
"fmt"
"time"

"github.com/glenvan/ttl"
)

func ExampleMap() {
maxTTL := 300 * time.Millisecond // a key's time to live
defaultTTL := 300 * time.Millisecond // the default lifetime for a key/value pair
startSize := 3 // initial number of items in map
pruneInterval := 100 * time.Millisecond // prune expired items each time pruneInterval elapses
refreshLastAccessOnGet := true // update item's 'lastAccessTime' on ttl.Map.Load()
refreshOnLoad := true // update item's 'lastAccessTime' on ttl.Map.Load()

// Any comparable data type such as int, uint64, pointers and struct types (if all field
// types are comparable) can be used as the key type
t := ttl.NewMap[string, string](
context.Background(),
maxTTL,
startSize,
pruneInterval,
refreshLastAccessOnGet)
t := ttl.NewMap[string, string](defaultTTL, startSize, pruneInterval, refreshOnLoad)
defer t.Close()

// Populate the ttl.Map
Expand All @@ -38,7 +32,7 @@ func ExampleMap() {
return true
})

sleepTime := maxTTL + pruneInterval
sleepTime := defaultTTL + pruneInterval
fmt.Printf("Sleeping %s, items should be expired and removed afterward\n", sleepTime)

time.Sleep(sleepTime)
Expand All @@ -60,7 +54,7 @@ func ExampleMap() {
}

func ExampleMap_Load() {
tm := ttl.NewMap[string, string](context.Background(), 30*time.Second, 0, 2*time.Second, true)
tm := ttl.NewMap[string, string](30*time.Second, 0, 2*time.Second, true)
defer tm.Close()

tm.Store("hello", "world")
Expand All @@ -74,7 +68,7 @@ func ExampleMap_Load() {
}

func ExampleMap_Range() {
tm := ttl.NewMap[string, string](context.Background(), 30*time.Second, 0, 2*time.Second, true)
tm := ttl.NewMap[string, string](30*time.Second, 0, 2*time.Second, true)
defer tm.Close()

tm.Store("hello", "world")
Expand All @@ -95,6 +89,7 @@ func ExampleMap_Range() {
return true // continue
})

// Give the goroutine some time to complete
time.Sleep(20 * time.Millisecond)

fmt.Printf("Length after: %d\n", tm.Length())
Expand All @@ -104,7 +99,7 @@ func ExampleMap_Range() {
}

func ExampleMap_DeleteFunc() {
tm := ttl.NewMap[string, int](context.Background(), 30*time.Second, 0, 2*time.Second, true)
tm := ttl.NewMap[string, int](30*time.Second, 0, 2*time.Second, true)
defer tm.Close()

tm.Store("zero", 0)
Expand Down
Loading

0 comments on commit 46e0b4f

Please sign in to comment.