Skip to content

Commit f9bdeb0

Browse files
committed
refactor: improve code structure
1 parent d5aa4f6 commit f9bdeb0

File tree

2 files changed

+114
-110
lines changed

2 files changed

+114
-110
lines changed

dataloader.go

Lines changed: 86 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,6 @@ import (
1414
// Loader is the function type for loading data
1515
type Loader[K comparable, V any] func(context.Context, []K) []Result[V]
1616

17-
// config holds the configuration for DataLoader
18-
type config struct {
19-
// BatchSize is the number of keys to batch together, Default is 100
20-
BatchSize int
21-
// Wait is the duration to wait before processing a batch, Default is 16ms
22-
Wait time.Duration
23-
// CacheSize is the size of the cache, Default is 1024
24-
CacheSize int
25-
// CacheExpire is the duration to expire cache items, Default is 1 minute
26-
CacheExpire time.Duration
27-
}
28-
29-
// dataLoader is the main struct for the dataloader
30-
type dataLoader[K comparable, V any] struct {
31-
loader Loader[K, V]
32-
cache *expirable.LRU[K, V]
33-
config config
34-
mu sync.Mutex
35-
batch []K
36-
chs map[K][]chan Result[V]
37-
stopSchedule chan struct{}
38-
}
39-
4017
// Interface is a `DataLoader` Interface which defines a public API for loading data from a particular
4118
// data back-end with unique keys such as the `id` column of a SQL table or
4219
// document name in a MongoDB database, given a batch loading function.
@@ -60,6 +37,29 @@ type Interface[K comparable, V any] interface {
6037
Prime(ctx context.Context, key K, value V) Interface[K, V]
6138
}
6239

40+
// config holds the configuration for DataLoader
41+
type config struct {
42+
// BatchSize is the number of keys to batch together, Default is 100
43+
BatchSize int
44+
// Wait is the duration to wait before processing a batch, Default is 16ms
45+
Wait time.Duration
46+
// CacheSize is the size of the cache, Default is 1024
47+
CacheSize int
48+
// CacheExpire is the duration to expire cache items, Default is 1 minute
49+
CacheExpire time.Duration
50+
}
51+
52+
// dataLoader is the main struct for the dataloader
53+
type dataLoader[K comparable, V any] struct {
54+
loader Loader[K, V]
55+
cache *expirable.LRU[K, V]
56+
config config
57+
mu sync.Mutex
58+
batch []K
59+
chs map[K][]chan Result[V]
60+
stopSchedule chan struct{}
61+
}
62+
6363
// New creates a new DataLoader with the given loader function and options
6464
func New[K comparable, V any](loader Loader[K, V], options ...Option) Interface[K, V] {
6565
config := config{
@@ -88,29 +88,68 @@ func New[K comparable, V any](loader Loader[K, V], options ...Option) Interface[
8888
return dl
8989
}
9090

91-
// Option is a function type for configuring DataLoader
92-
type Option func(*config)
91+
// Load loads a single key
92+
func (d *dataLoader[K, V]) Load(ctx context.Context, key K) Result[V] {
93+
return <-d.goLoad(ctx, key)
94+
}
95+
96+
// LoadMany loads multiple keys
97+
func (d *dataLoader[K, V]) LoadMany(ctx context.Context, keys []K) []Result[V] {
98+
chs := make([]<-chan Result[V], len(keys))
99+
for i, key := range keys {
100+
chs[i] = d.goLoad(ctx, key)
101+
}
102+
103+
results := make([]Result[V], len(keys))
104+
for i, ch := range chs {
105+
results[i] = <-ch
106+
}
107+
108+
return results
109+
}
110+
111+
// LoadMap loads multiple keys and returns a map of results
112+
func (d *dataLoader[K, V]) LoadMap(ctx context.Context, keys []K) map[K]Result[V] {
113+
chs := make([]<-chan Result[V], len(keys))
114+
for i, key := range keys {
115+
chs[i] = d.goLoad(ctx, key)
116+
}
117+
118+
results := make(map[K]Result[V], len(keys))
119+
for i, ch := range chs {
120+
results[keys[i]] = <-ch
121+
}
93122

94-
// WithCache sets the cache size for the DataLoader
95-
func WithCache(size int, expire time.Duration) Option {
96-
return func(c *config) {
97-
c.CacheSize = size
98-
c.CacheExpire = expire
123+
return results
124+
}
125+
126+
// Clear removes an item from the cache
127+
func (d *dataLoader[K, V]) Clear(key K) Interface[K, V] {
128+
if d.cache != nil {
129+
d.cache.Remove(key)
99130
}
131+
132+
return d
100133
}
101134

102-
// WithBatchSize sets the batch size for the DataLoader
103-
func WithBatchSize(size int) Option {
104-
return func(c *config) {
105-
c.BatchSize = size
135+
// ClearAll clears the entire cache
136+
func (d *dataLoader[K, V]) ClearAll() Interface[K, V] {
137+
if d.cache != nil {
138+
d.cache.Purge()
106139
}
140+
141+
return d
107142
}
108143

109-
// WithWait sets the wait duration for the DataLoader
110-
func WithWait(wait time.Duration) Option {
111-
return func(c *config) {
112-
c.Wait = wait
144+
// Prime primes the cache with a key and value
145+
func (d *dataLoader[K, V]) Prime(ctx context.Context, key K, value V) Interface[K, V] {
146+
if d.cache != nil {
147+
if _, ok := d.cache.Get(key); ok {
148+
d.cache.Add(key, value)
149+
}
113150
}
151+
152+
return d
114153
}
115154

116155
// goLoad loads a single key asynchronously
@@ -160,47 +199,6 @@ func (d *dataLoader[K, V]) goLoad(ctx context.Context, key K) <-chan Result[V] {
160199
return ch
161200
}
162201

163-
// Load loads a single key
164-
func (d *dataLoader[K, V]) Load(ctx context.Context, key K) Result[V] {
165-
return <-d.goLoad(ctx, key)
166-
}
167-
168-
// LoadMany loads multiple keys
169-
func (d *dataLoader[K, V]) LoadMany(ctx context.Context, keys []K) []Result[V] {
170-
chs := make([]<-chan Result[V], len(keys))
171-
for i, key := range keys {
172-
chs[i] = d.goLoad(ctx, key)
173-
}
174-
175-
results := make([]Result[V], len(keys))
176-
for i, ch := range chs {
177-
results[i] = <-ch
178-
}
179-
180-
return results
181-
}
182-
183-
// LoadMap loads multiple keys and returns a map of results
184-
func (d *dataLoader[K, V]) LoadMap(ctx context.Context, keys []K) map[K]Result[V] {
185-
chs := make([]<-chan Result[V], len(keys))
186-
for i, key := range keys {
187-
chs[i] = d.goLoad(ctx, key)
188-
}
189-
190-
results := make(map[K]Result[V], len(keys))
191-
for i, ch := range chs {
192-
results[keys[i]] = <-ch
193-
}
194-
195-
return results
196-
}
197-
198-
// reset resets the DataLoader
199-
func (d *dataLoader[K, V]) reset() {
200-
d.batch = make([]K, 0, d.config.BatchSize)
201-
d.chs = make(map[K][]chan Result[V], d.config.BatchSize)
202-
}
203-
204202
// scheduleBatch schedules a batch to be processed
205203
func (d *dataLoader[K, V]) scheduleBatch(ctx context.Context, stopSchedule <-chan struct{}) {
206204
select {
@@ -223,11 +221,12 @@ func (d *dataLoader[K, V]) processBatch(ctx context.Context, keys []K, chs map[K
223221
const size = 64 << 10
224222
buf := make([]byte, size)
225223
buf = buf[:runtime.Stack(buf, false)]
226-
fmt.Fprintf(os.Stderr, "Dataloader: Panic received in loader function: %v\n%s", r, buf)
224+
err := fmt.Errorf("dataloader: panic received in loader function: %v", r)
225+
fmt.Fprintf(os.Stderr, "%v\n%s", err, buf)
227226

228227
for _, chs := range chs {
229228
for _, ch := range chs {
230-
ch <- Result[V]{err: fmt.Errorf("panic received in loader function: %v", r)}
229+
ch <- Result[V]{err: err}
231230
close(ch)
232231
}
233232
}
@@ -245,39 +244,16 @@ func (d *dataLoader[K, V]) processBatch(ctx context.Context, keys []K, chs map[K
245244
}
246245
}
247246

247+
// reset resets the DataLoader
248+
func (d *dataLoader[K, V]) reset() {
249+
d.batch = make([]K, 0, d.config.BatchSize)
250+
d.chs = make(map[K][]chan Result[V], d.config.BatchSize)
251+
}
252+
248253
// sendResult sends a result to channels
249254
func sendResult[V any](chs []chan Result[V], result Result[V]) {
250255
for _, ch := range chs {
251256
ch <- result
252257
close(ch)
253258
}
254259
}
255-
256-
// Clear removes an item from the cache
257-
func (d *dataLoader[K, V]) Clear(key K) Interface[K, V] {
258-
if d.cache != nil {
259-
d.cache.Remove(key)
260-
}
261-
262-
return d
263-
}
264-
265-
// ClearAll clears the entire cache
266-
func (d *dataLoader[K, V]) ClearAll() Interface[K, V] {
267-
if d.cache != nil {
268-
d.cache.Purge()
269-
}
270-
271-
return d
272-
}
273-
274-
// Prime primes the cache with a key and value
275-
func (d *dataLoader[K, V]) Prime(ctx context.Context, key K, value V) Interface[K, V] {
276-
if d.cache != nil {
277-
if _, ok := d.cache.Get(key); ok {
278-
d.cache.Add(key, value)
279-
}
280-
}
281-
282-
return d
283-
}

option.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package dataloader
2+
3+
import "time"
4+
5+
// Option is a function type for configuring DataLoader
6+
type Option func(*config)
7+
8+
// WithCache sets the cache size for the DataLoader
9+
func WithCache(size int, expire time.Duration) Option {
10+
return func(c *config) {
11+
c.CacheSize = size
12+
c.CacheExpire = expire
13+
}
14+
}
15+
16+
// WithBatchSize sets the batch size for the DataLoader
17+
func WithBatchSize(size int) Option {
18+
return func(c *config) {
19+
c.BatchSize = size
20+
}
21+
}
22+
23+
// WithWait sets the wait duration for the DataLoader
24+
func WithWait(wait time.Duration) Option {
25+
return func(c *config) {
26+
c.Wait = wait
27+
}
28+
}

0 commit comments

Comments
 (0)