Skip to content

Commit 97fa9dd

Browse files
committed
Add context to fetch functions
close vikstrous#11
1 parent 37c017c commit 97fa9dd

File tree

6 files changed

+44
-37
lines changed

6 files changed

+44
-37
lines changed

README.md

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ It works as follows:
99
* Each of many concurrently executing graphql resolver functions call Load() on the Loader object with different keys. Let's say K1, K2, K3
1010
* Each call to Load() with a new key is delayed slightly (a few milliseconds) so that the Loader can load them together.
1111
* The customizable fetch function of the loader takes a list of keys and loads data for all of them in a single batched request to the data storage layer. It might send `[K1,K2,K3]` and get back `[V1,V2,V3]`.
12-
* The Loader takes case of sending the right resight to the right caller and the result is cached for the duration of the graphql request.
12+
* The Loader takes case of sending the right result to the right caller and the result is cached for the duration of the graphql request.
1313

1414
Usage:
1515

@@ -31,7 +31,8 @@ import (
3131
)
3232

3333
// fetchFn is shown as a function here, but it might work better as a method
34-
func fetchFn(keys []string) (ret []int, errs []error) {
34+
// ctx is the context from the first call to Load for the current batch
35+
func fetchFn(ctx context.Context, keys []string) (ret []int, errs []error) {
3536
for _, key := range keys {
3637
num, err := strconv.ParseInt(key, 10, 32)
3738
ret = append(ret, int(num))
@@ -61,18 +62,20 @@ func main() {
6162
Benchmarks show that this package is faster than both of the above and I find it easier to use.
6263

6364
```
64-
BenchmarkDataloader/caches-8 4152324 270.3 ns/op 168 B/op 5 allocs/op
65-
BenchmarkDataloader/random_spread-8 1000000 1281 ns/op 626 B/op 11 allocs/op
66-
BenchmarkDataloader/concurently-8 33159 55575 ns/op 32649 B/op 160 allocs/op
67-
BenchmarkDataloader/all_in_one_request-8 10000 7556166 ns/op 2574411 B/op 60032 allocs/op
68-
69-
BenchmarkDataloaden/caches-8 17960090 67.73 ns/op 24 B/op 1 allocs/op
70-
BenchmarkDataloaden/random_spread-8 1223949 955.0 ns/op 279 B/op 5 allocs/op
71-
BenchmarkDataloaden/concurently-8 27093 43594 ns/op 2867 B/op 76 allocs/op
72-
BenchmarkDataloaden/all_in_one_request-8 10000 1410499 ns/op 487876 B/op 10007 allocs/op
73-
74-
BenchmarkDataloadgen/caches-8 22032517 53.61 ns/op 8 B/op 0 allocs/op
75-
BenchmarkDataloadgen/random_spread-8 2558128 483.7 ns/op 287 B/op 4 allocs/op
76-
BenchmarkDataloadgen/concurently-8 31900 34903 ns/op 2906 B/op 71 allocs/op
77-
BenchmarkDataloadgen/all_in_one_request-8 10000 1032841 ns/op 573619 B/op 7 allocs/op
78-
```
65+
BenchmarkDataloader/caches-8 4512438 267.7 ns/op 168 B/op 5 allocs/op
66+
BenchmarkDataloader/random_spread-8 1000000 1267 ns/op 626 B/op 11 allocs/op
67+
BenchmarkDataloader/concurently-8 16305 78332 ns/op 28619 B/op 154 allocs/op
68+
BenchmarkDataloader/all_in_one_request-8 10000 7081367 ns/op 2576182 B/op 60031 allocs/op
69+
70+
BenchmarkDataloaden/caches-8 18537674 68.09 ns/op 24 B/op 1 allocs/op
71+
BenchmarkDataloaden/random_spread-8 2885696 696.8 ns/op 295 B/op 5 allocs/op
72+
BenchmarkDataloaden/concurently-8 23302 50965 ns/op 2799 B/op 75 allocs/op
73+
BenchmarkDataloaden/all_in_one_request-8 10000 1279412 ns/op 487867 B/op 10007 allocs/op
74+
75+
BenchmarkDataloadgen/caches-8 23384140 53.00 ns/op 8 B/op 0 allocs/op
76+
BenchmarkDataloadgen/random_spread-8 2394097 604.9 ns/op 292 B/op 4 allocs/op
77+
BenchmarkDataloadgen/concurently-8 28359 39131 ns/op 2852 B/op 68 allocs/op
78+
BenchmarkDataloadgen/all_in_one_request-8 10000 1041825 ns/op 573643 B/op 8 allocs/op
79+
```
80+
81+
To run the benchmarks, run `go test -bench=. . -benchmem` from the benchmark directory.

benchmark/benchmarks_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func (k IntKey) Raw() interface{} { return k }
114114

115115
func BenchmarkDataloadgen(b *testing.B) {
116116
ctx := context.Background()
117-
dl := dataloadgen.NewLoader(func(keys []int) ([]benchmarkUser, []error) {
117+
dl := dataloadgen.NewLoader(func(_ context.Context, keys []int) ([]benchmarkUser, []error) {
118118
users := make([]benchmarkUser, len(keys))
119119
errors := make([]error, len(keys))
120120

dataloaden_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func TestUserLoader(t *testing.T) {
2020
ctx := context.Background()
2121
var fetches [][]string
2222
var mu sync.Mutex
23-
dl := dataloadgen.NewLoader(func(keys []string) ([]*benchmarkUser, []error) {
23+
dl := dataloadgen.NewLoader(func(_ context.Context, keys []string) ([]*benchmarkUser, []error) {
2424
mu.Lock()
2525
fetches = append(fetches, keys)
2626
mu.Unlock()

dataloader_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
func BenchmarkLoaderFromDataloader(b *testing.B) {
1717
a := &Avg{}
1818
ctx := context.Background()
19-
dl := dataloadgen.NewLoader(func(keys []string) (results []string, errs []error) {
19+
dl := dataloadgen.NewLoader(func(_ context.Context, keys []string) (results []string, errs []error) {
2020
a.Add(len(keys))
2121
results = make([]string, 0, len(keys))
2222
for _, key := range keys {
@@ -485,7 +485,7 @@ func TestLoader(t *testing.T) {
485485
func IDLoader(max int) (*dataloadgen.Loader[string, string], *[][]string) {
486486
var mu sync.Mutex
487487
var loadCalls [][]string
488-
identityLoader := dataloadgen.NewLoader(func(keys []string) (results []string, errs []error) {
488+
identityLoader := dataloadgen.NewLoader(func(_ context.Context, keys []string) (results []string, errs []error) {
489489
mu.Lock()
490490
loadCalls = append(loadCalls, keys)
491491
mu.Unlock()
@@ -500,7 +500,7 @@ func IDLoader(max int) (*dataloadgen.Loader[string, string], *[][]string) {
500500
func BatchOnlyLoader(max int) (*dataloadgen.Loader[string, string], *[][]string) {
501501
var mu sync.Mutex
502502
var loadCalls [][]string
503-
identityLoader := dataloadgen.NewLoader(func(keys []string) (results []string, errs []error) {
503+
identityLoader := dataloadgen.NewLoader(func(_ context.Context, keys []string) (results []string, errs []error) {
504504
mu.Lock()
505505
loadCalls = append(loadCalls, keys)
506506
mu.Unlock()
@@ -515,7 +515,7 @@ func BatchOnlyLoader(max int) (*dataloadgen.Loader[string, string], *[][]string)
515515
func ErrorLoader(max int) (*dataloadgen.Loader[string, string], *[][]string) {
516516
var mu sync.Mutex
517517
var loadCalls [][]string
518-
identityLoader := dataloadgen.NewLoader(func(keys []string) (results []string, errs []error) {
518+
identityLoader := dataloadgen.NewLoader(func(_ context.Context, keys []string) (results []string, errs []error) {
519519
mu.Lock()
520520
loadCalls = append(loadCalls, keys)
521521
mu.Unlock()
@@ -531,7 +531,7 @@ func ErrorLoader(max int) (*dataloadgen.Loader[string, string], *[][]string) {
531531
func OneErrorLoader(max int) (*dataloadgen.Loader[string, string], *[][]string) {
532532
var mu sync.Mutex
533533
var loadCalls [][]string
534-
identityLoader := dataloadgen.NewLoader(func(keys []string) (results []string, errs []error) {
534+
identityLoader := dataloadgen.NewLoader(func(_ context.Context, keys []string) (results []string, errs []error) {
535535
results = make([]string, max)
536536
errs = make([]error, max)
537537
mu.Lock()
@@ -552,7 +552,7 @@ func OneErrorLoader(max int) (*dataloadgen.Loader[string, string], *[][]string)
552552

553553
func PanicLoader(max int) (*dataloadgen.Loader[string, string], *[][]string) {
554554
var loadCalls [][]string
555-
panicLoader := dataloadgen.NewLoader(func(keys []string) (results []string, errs []error) {
555+
panicLoader := dataloadgen.NewLoader(func(_ context.Context, keys []string) (results []string, errs []error) {
556556
panic("Programming error")
557557
}, dataloadgen.WithBatchCapacity(max)) //, withSilentLogger())
558558
return panicLoader, &loadCalls
@@ -561,7 +561,7 @@ func PanicLoader(max int) (*dataloadgen.Loader[string, string], *[][]string) {
561561
func BadLoader(max int) (*dataloadgen.Loader[string, string], *[][]string) {
562562
var mu sync.Mutex
563563
var loadCalls [][]string
564-
identityLoader := dataloadgen.NewLoader(func(keys []string) (results []string, errs []error) {
564+
identityLoader := dataloadgen.NewLoader(func(_ context.Context, keys []string) (results []string, errs []error) {
565565
mu.Lock()
566566
loadCalls = append(loadCalls, keys)
567567
mu.Unlock()
@@ -575,7 +575,7 @@ func NoCacheLoader(max int) (*dataloadgen.Loader[string, string], *[][]string) {
575575
var mu sync.Mutex
576576
var loadCalls [][]string
577577
// cache := &NoCache{}
578-
identityLoader := dataloadgen.NewLoader(func(keys []string) (results []string, errs []error) {
578+
identityLoader := dataloadgen.NewLoader(func(_ context.Context, keys []string) (results []string, errs []error) {
579579
mu.Lock()
580580
loadCalls = append(loadCalls, keys)
581581
mu.Unlock()
@@ -592,7 +592,7 @@ func FaultyLoader() (*dataloadgen.Loader[string, string], *[][]string) {
592592
var mu sync.Mutex
593593
var loadCalls [][]string
594594

595-
loader := dataloadgen.NewLoader(func(keys []string) (results []string, errs []error) {
595+
loader := dataloadgen.NewLoader(func(_ context.Context, keys []string) (results []string, errs []error) {
596596
mu.Lock()
597597
loadCalls = append(loadCalls, keys)
598598
mu.Unlock()

dataloadgen.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func WithTracer(tracer trace.Tracer) Option {
3535
}
3636

3737
// NewLoader creates a new GenericLoader given a fetch, wait, and maxBatch
38-
func NewLoader[KeyT comparable, ValueT any](fetch func(keys []KeyT) ([]ValueT, []error), options ...Option) *Loader[KeyT, ValueT] {
38+
func NewLoader[KeyT comparable, ValueT any](fetch func(ctx context.Context, keys []KeyT) ([]ValueT, []error), options ...Option) *Loader[KeyT, ValueT] {
3939
config := &loaderConfig{
4040
wait: 16 * time.Millisecond,
4141
maxBatch: 0, // unlimited
@@ -64,7 +64,7 @@ type loaderConfig struct {
6464
// Loader batches and caches requests
6565
type Loader[KeyT comparable, ValueT any] struct {
6666
// this method provides the data for the loader
67-
fetch func(keys []KeyT) ([]ValueT, []error)
67+
fetch func(ctx context.Context, keys []KeyT) ([]ValueT, []error)
6868

6969
*loaderConfig
7070

@@ -87,6 +87,7 @@ type loaderBatch[KeyT comparable, ValueT any] struct {
8787
errors []error
8888
fetchExecuted bool
8989
done chan struct{}
90+
firstContext context.Context
9091
contexts []context.Context
9192
spans []trace.Span
9293
}
@@ -106,7 +107,7 @@ func (l *Loader[KeyT, ValueT]) LoadThunk(ctx context.Context, key KeyT) func() (
106107
return it
107108
}
108109

109-
l.startBatch()
110+
l.startBatch(ctx)
110111

111112
if l.tracer != nil {
112113
_, loadSpan := l.tracer.Start(ctx, "dataloadgen.load")
@@ -230,9 +231,12 @@ func (l *Loader[KeyT, ValueT]) Clear(key KeyT) {
230231
l.mu.Unlock()
231232
}
232233

233-
func (l *Loader[KeyT, ValueT]) startBatch() {
234+
func (l *Loader[KeyT, ValueT]) startBatch(ctx context.Context) {
234235
if l.batch == nil {
235-
batch := &loaderBatch[KeyT, ValueT]{done: make(chan struct{})}
236+
batch := &loaderBatch[KeyT, ValueT]{
237+
done: make(chan struct{}),
238+
firstContext: ctx,
239+
}
236240
l.batch = batch
237241
go func(l *Loader[KeyT, ValueT]) {
238242
time.Sleep(l.wait)
@@ -257,7 +261,7 @@ func (l *Loader[KeyT, ValueT]) startBatch() {
257261
}
258262
}
259263

260-
batch.results, batch.errors = l.fetch(batch.keys)
264+
batch.results, batch.errors = l.fetch(batch.firstContext, batch.keys)
261265

262266
if l.tracer != nil {
263267
for _, span := range spans {
@@ -289,7 +293,7 @@ func (l *Loader[KeyT, ValueT]) addKeyToBatch(b *loaderBatch[KeyT, ValueT], key K
289293
}
290294
}
291295

292-
b.results, b.errors = l.fetch(b.keys)
296+
b.results, b.errors = l.fetch(b.firstContext, b.keys)
293297

294298
if l.tracer != nil {
295299
for _, span := range spans {

dataloadgen_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313

1414
func ExampleLoader() {
1515
ctx := context.Background()
16-
loader := dataloadgen.NewLoader(func(keys []string) (ret []int, errs []error) {
16+
loader := dataloadgen.NewLoader(func(ctx context.Context, keys []string) (ret []int, errs []error) {
1717
for _, key := range keys {
1818
num, err := strconv.ParseInt(key, 10, 32)
1919
ret = append(ret, int(num))
@@ -36,7 +36,7 @@ func TestEdgeCases(t *testing.T) {
3636
ctx := context.Background()
3737
var fetches [][]int
3838
var mu sync.Mutex
39-
dl := dataloadgen.NewLoader(func(keys []int) ([]string, []error) {
39+
dl := dataloadgen.NewLoader(func(_ context.Context, keys []int) ([]string, []error) {
4040
mu.Lock()
4141
fetches = append(fetches, keys)
4242
mu.Unlock()

0 commit comments

Comments
 (0)