Skip to content

Commit 8720313

Browse files
committed
cache: Add a simple partitioned lazy cache
1 parent a91c270 commit 8720313

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
lines changed

‎cache/partitioned_lazy_cache.go‎

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright 2017-present The Hugo Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package cache
15+
16+
import (
17+
"sync"
18+
)
19+
20+
// Partition represents a cache partition where Load is the callback
21+
// for when the partition is needed.
22+
type Partition struct {
23+
Key string
24+
Load func() (map[string]interface{}, error)
25+
}
26+
27+
type lazyPartition struct {
28+
initSync sync.Once
29+
cache map[string]interface{}
30+
load func() (map[string]interface{}, error)
31+
}
32+
33+
func (l *lazyPartition) init() error {
34+
var err error
35+
l.initSync.Do(func() {
36+
var c map[string]interface{}
37+
c, err = l.load()
38+
l.cache = c
39+
})
40+
41+
return err
42+
}
43+
44+
// PartitionedLazyCache is a lazily loaded cache paritioned by a supplied string key.
45+
type PartitionedLazyCache struct {
46+
partitions map[string]*lazyPartition
47+
}
48+
49+
// NewPartitionedLazyCache creates a new NewPartitionedLazyCache with the supplied
50+
// partitions.
51+
func NewPartitionedLazyCache(partitions ...Partition) *PartitionedLazyCache {
52+
lazyPartitions := make(map[string]*lazyPartition, len(partitions))
53+
for _, partition := range partitions {
54+
lazyPartitions[partition.Key] = &lazyPartition{load: partition.Load}
55+
}
56+
cache := &PartitionedLazyCache{partitions: lazyPartitions}
57+
58+
return cache
59+
}
60+
61+
// Get initializes the partition if not already done so, then looks up the given
62+
// key in the given partition, returns nil if no value found.
63+
func (c *PartitionedLazyCache) Get(partition, key string) (interface{}, error) {
64+
p, found := c.partitions[partition]
65+
66+
if !found {
67+
return nil, nil
68+
}
69+
70+
if err := p.init(); err != nil {
71+
return nil, err
72+
}
73+
74+
if v, found := p.cache[key]; found {
75+
return v, nil
76+
}
77+
78+
return nil, nil
79+
80+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright 2017-present The Hugo Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package cache
15+
16+
import (
17+
"errors"
18+
"testing"
19+
20+
"github.com/stretchr/testify/require"
21+
)
22+
23+
func TestNewPartitionedLazyCache(t *testing.T) {
24+
t.Parallel()
25+
26+
assert := require.New(t)
27+
28+
p1 := Partition{
29+
Key: "p1",
30+
Load: func() (map[string]interface{}, error) {
31+
return map[string]interface{}{
32+
"p1_1": "p1v1",
33+
"p1_2": "p1v2",
34+
"p1_nil": nil,
35+
}, nil
36+
},
37+
}
38+
39+
p2 := Partition{
40+
Key: "p2",
41+
Load: func() (map[string]interface{}, error) {
42+
return map[string]interface{}{
43+
"p2_1": "p2v1",
44+
"p2_2": "p2v2",
45+
"p2_3": "p2v3",
46+
}, nil
47+
},
48+
}
49+
50+
cache := NewPartitionedLazyCache(p1, p2)
51+
52+
v, err := cache.Get("p1", "p1_1")
53+
assert.NoError(err)
54+
assert.Equal("p1v1", v)
55+
56+
v, err = cache.Get("p1", "p2_1")
57+
assert.NoError(err)
58+
assert.Nil(v)
59+
60+
v, err = cache.Get("p1", "p1_nil")
61+
assert.NoError(err)
62+
assert.Nil(v)
63+
64+
v, err = cache.Get("p2", "p2_3")
65+
assert.NoError(err)
66+
assert.Equal("p2v3", v)
67+
68+
v, err = cache.Get("doesnotexist", "p1_1")
69+
assert.NoError(err)
70+
assert.Nil(v)
71+
72+
v, err = cache.Get("p1", "doesnotexist")
73+
assert.NoError(err)
74+
assert.Nil(v)
75+
76+
errorP := Partition{
77+
Key: "p3",
78+
Load: func() (map[string]interface{}, error) {
79+
return nil, errors.New("Failed")
80+
},
81+
}
82+
83+
cache = NewPartitionedLazyCache(errorP)
84+
85+
v, err = cache.Get("p1", "doesnotexist")
86+
assert.NoError(err)
87+
assert.Nil(v)
88+
89+
_, err = cache.Get("p3", "doesnotexist")
90+
assert.Error(err)
91+
92+
}

0 commit comments

Comments
 (0)