{"id":15065883,"url":"https://github.com/andy9775/dataloader","last_synced_at":"2026-02-09T15:34:14.988Z","repository":{"id":57500084,"uuid":"141077282","full_name":"andy9775/dataloader","owner":"andy9775","description":"A golang implementation of a GraphQL dataloader  featuring capacity based loading","archived":false,"fork":false,"pushed_at":"2023-10-11T20:50:15.000Z","size":103,"stargazers_count":6,"open_issues_count":2,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-23T01:02:18.524Z","etag":null,"topics":["go","golang","golang-library","graphql","graphql-server","graphql-tools"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andy9775.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-07-16T02:32:28.000Z","updated_at":"2023-12-30T11:30:57.000Z","dependencies_parsed_at":"2025-02-17T09:31:36.016Z","dependency_job_id":"04ebaa83-b860-44e0-b5e7-58d5441c4cd8","html_url":"https://github.com/andy9775/dataloader","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andy9775%2Fdataloader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andy9775%2Fdataloader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andy9775%2Fdataloader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andy9775%2Fdataloader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andy9775","download_url":"https://codeload.github.com/andy9775/dataloader/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248225870,"owners_count":21068078,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang","golang-library","graphql","graphql-server","graphql-tools"],"created_at":"2024-09-25T00:56:46.923Z","updated_at":"2026-02-09T15:34:09.950Z","avatar_url":"https://github.com/andy9775.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DataLoader\n\n[![CircleCI](https://circleci.com/gh/andy9775/dataloader.svg?style=svg)](https://circleci.com/gh/andy9775/dataloader)\n[![Coverage Status](https://coveralls.io/repos/github/andy9775/dataloader/badge.svg?branch=master)](https://coveralls.io/github/andy9775/dataloader?branch=master)\n[![Go Report Card](https://goreportcard.com/badge/github.com/andy9775/dataloader)](https://goreportcard.com/report/github.com/andy9775/dataloader)\n\nDataLoader implements a counter which can be used against Field Resolver\nfunctions. It calls a **batch** function after the number of calls to Load values\nreaches the loaders capacity.\n\n##### Terminology\n\nThe following terms are used throughout the documentation:\n\n- **Element** - Refers to the item to be resolved. This can be a record from a\n  database table or GraphQL type.\n\n##### Implementation/Usage\n\nUse the DataLoader when fetching a known number of elements, where each element has\na field resolver associated with it that hits a database or has some other time\nconsuming operation to resolve the data. This is typically useful when making\n_index_ type queries where _n_ number of root elements is requested and each root\nelement has an associated model to be fetched.\n\nFor example, for a `users` table which contains:\n\n- first_name\n- last_name\n- status_id (foreign key - status table)\n\nand a `status` table which contains:\n\n- id\n- value (string)\n\nPerforming a query like:\n\n```\n{\n  users(num: 10) {\n    first_name\n    status {\n      value\n    }\n  }\n}\n```\n\nwhere the `users` resolver returns an array of users (10 in this case). This\nwill typically result in 1 call to return all 10 users, and 10 calls to resolve\nthe `status` field for each user.\n\nUse the DataLoader by setting its capacity to _n_ (10 in this case) and\nproviding a batch loader function which accepts the keys and should return\n_n_ number of `status` records. The result of which is a single call to\nthe database to return the `status` elements after number of calls to `Load()`\nhas hit the set capacity.\n\nNote that the capacity also acts as a _floor_. In instances where _at least_ _n_\ncalls are known, all _n+1_ calls are executed depending on the\n[strategy](#Strategies) used.\n\nInternally, the DataLoader waits for the `Load()` function to be called _n_ times,\nwhere _n_ is the initial DataLoader capacity. The `Load()` function returns a\nThunk, or ThunkMany function, which block when called until the number of calls\nto Load equal the loaders capacity. Each call to the returned Thunk function\nthen returns the values for the keys it is attached to.\n\n## API\n\n#### DataLoader\n\n\u003e DataLoader is the basic interface for the library. It contains a provided\n\u003e strategy and cache strategy.\n\n**`NewDataLoader(int, BatchFunction, func(int, BatchFunction) Strategy, ...Option) DataLoader`**\u003cbr\u003e\nNewDataLoader returns a new instance of a DataLoader tracking to the capacity\nprovided and using the provided execution and cache strategy. The second argument\nshould return a strategy which accepts a capacity value and the BatchFunction\n\n**`Load(context.Context, Key) Thunk`**\u003cbr\u003e\nReturns a Thunk for the specified keys. Internally Load adds the\nprovided keys to the keys array and returns a callback function which when\ncalled returns the values for the provided keys. Load does not block callers.\n\n**`LoadMany(context.Context, ...Key) ThunkMany`**\u003cbr\u003e\nReturns a ThunkMany for the specified keys. Internally LoadMany adds the\nprovided keys to the keys array and returns a callback function which when\ncalled returns the values for the provided keys. LoadMany does not block\ncallers.\n\nThe options include:\n\n**`WithCache(Cache) Option`**\u003cbr\u003e\nWithCache sets the provided cache strategy on the loader\n\n**`WithTracer(Cache) Option`**\u003cbr\u003e\nWithTracer sets the provided tracer on the loader\n\n#### Strategy\n\n\u003e Strategy is a interface to be used by implementors to hold and track data.\n\n**`Load(context.Context, Key) Thunk`**\u003cbr\u003e\nLoad should return the Thunk function linked to the provided key. Load should\nnot reference a cache nor should it block.\n\n**`LoadMany(context.Context, ...Key) ThunkMany`**\u003cbr\u003e\nLoadMany should return a ThunkMany function linked to the provided keys.\nLoadMany should not reference a cache nor should it block.\n\n**`LoadNoOp(context.Context) ResultMap`**\u003cbr\u003e\nLoadNoOp should not block the caller nor return values to the caller. It is\ncalled when a value is retrieved from the cache and it's responsibility is to\nincrement the internal loads counter.\n\n#### Sozu Strategy\n\n\u003e The sozu strategy batches all calls to the batch function, including _n+1_\n\u003e calls. Since the strategy returns a Thunk or ThunkMany, calling Load or\n\u003e LoadMany before performing another long running process will allow the batch\n\u003e function to run concurrently to any other operations.\n\n**`NewSozuStrategy(Options) func(int, BatchFunction) Strategy`**\u003cbr\u003e\nNewSozuStrategy returns a function which returns a new instance of a sozu\nstrategy for the provided capacity.\n\nThe Options values include:\n\n**`WithTimeout(time.Duration) Option`**\u003cbr\u003e\nWithTimeout sets the configured timeout on the strategy. `Default to 16 milliseconds`\n\n#### Standard Strategy\n\n\u003e The standard strategy batches the first calls to the batch function, all\n\u003e subsequent callers call the batch function directly. Since the strategy\n\u003e returns a Thunk or ThunkMany, calling Load or LoadMany before performing\n\u003e another long running process will allow the batch function to run concurrently\n\u003e to any other operations.\n\n**`NewStandardStrategy(Options) func(int, BatchFunction) Strategy`**\u003cbr\u003e\nNewStandardStrategy returns a function which returns a new instance of the\nstandard strategy for the provided capacity.\n\nThe Options include:\n\n**`WithTimeout(time.Duration) Option`**\u003cbr\u003e\nWithTimeout sets the configured timeout on the strategy. `Default to 16 milliseconds`\n\n#### Once Strategy\n\n\u003e The once strategy does not track calls to load and is useful for single calls\n\u003e to the batch function. Passing `InBackground: true` to the options allows the\n\u003e batch function to be called in the background, otherwise the batch function is\n\u003e called when Thunk, or ThunkMany, is called. It is useful for keeping a\n\u003e consistent API across an application and for automatically performing data\n\u003e queries in a background go routine.\n\n**`NewOnceStrategy(Options) func(int, BatchFunction) Strategy`**\u003cbr\u003e\nNewOnceStrategy returns a functions which returns an instance of the once\nstrategy ignoring the provided capacity value.\n\nThe Options include:\n\n**`WithInBackground() Option`**\u003cbr\u003e\nWithInBackground enables the batch function to execute in background on calls to\nLoad/LoadMany\n\n#### ResultMap\n\n\u003e ResultMap acts as a wrapper around a basic `map[string]Result` and provides\n\u003e accessor functions that tell the callers about the map or its state. ResultMap\n\u003e is not go routine safe.\n\u003e\n\u003e When creating a new instance of the result map, a capacity must be provided.\n\n**`NewResultMap(int) ResultMap`**\u003cbr\u003e\nNewResultMap returns a new instance of a result map with the set capacity\n\n**`Set(string, Result)`**\u003cbr\u003e\nSet sets the result in the result map\n\n**`GetValue(Key) Result`**\u003cbr\u003e\nGetValue returns the stored value for the provided key.\n\n**`GetValueForString(String) Result`**\u003cbr\u003e\nGetValue returns the stored value for the provided string value.\n\n**`Length() int`**\u003cbr\u003e\nLength returns the number of results it contains.\n\n**`Keys() []string`**\u003cbr\u003e\nKeys returns the keys used to identify the data within this result map.\n\n#### Key\n\n\u003e Key is an interface each element's identifier must implement. Each Key must be\n\u003e unique (therefore a good candidate is the primary key for a database entry).\n\n**`String() string`**\u003cbr\u003e\nString should return a string representation of the unique identifier. The\nresulting string is used in the `ResultMap` to map the element to it's\nidentifier.\n\n**`Raw() interface{}`**\u003cbr\u003e\nRaw should return the underlying value of the key. Examples are: `int`, `string`.\n\n#### Keys\n\n\u003e Keys wraps an array of keys and provides a way of tracking keys to\n\u003e be resolved by the batch function. It also provides methods to tell its state.\n\u003e Keys is not go routine safe.\n\n**`NewKeys(int) Keys`**\u003cbr\u003e\nNewKeys returns a new key store with it's length set to the capacity. If the\ncapacity is known to be exact provide the exact value. Otherwise adding a buffer\ncan be useful to prevent unnecessary memory growth.\n\n**`NewKeysWith(key ...Key) Keys`**\u003cbr\u003e\nNewKeysWith returns a Keys array with the provided keys.\n\n**`Append(...Key)`**\u003cbr\u003e\nAppend adds one or more keys to the internal array.\n\n**`Capacity() int`**\u003cbr\u003e\nCapacity returns the set capacity for the key array.\n\n**`Length() int`**\u003cbr\u003e\nLength returns the number of keys in the array.\n\n**`ClearAll()`**\u003cbr\u003e\nClearAll deletes all the stored keys from the key array.\n\n**`Keys() []interface{}`**\u003cbr\u003e\nKeys returns a unique array of interface{} types for each key after calling each\nkeys `Raw()` method\n\n**`IsEmpty() bool`**\u003cbr\u003e\nIsEmpty returns true if there are no keys in the keys array.\n\n#### Cache\n\n\u003e Cache provides an interface for caching strategies. The library provides a\n\u003e no-op caching strategy which **does not** store any values.\n\n**`NewNoOpCache() Cache`**\u003cbr\u003e\nNewNoOpCache returns an instance of a no operation cache. Internally it doesn't\nstore any values and all it's getter methods return nil or false.\n\n**`SetResult(context.Context, Key, Result)`**\u003cbr\u003e\nSetResult adds a value to the cache. The cache should store the value based on\nit's implementation.\n\n**`SetResultMap(context.Context, ResultMap)`**\u003cbr\u003e\nSetResultMap saves all the elements in the provided ResultMap\n\n**`GetResult(context.Context, Key) Result`**\u003cbr\u003e\nGetResult should return the result matching the key or nil if none are found.\n\n**`GetResultMap(context.Context, ...Key) ResultMap`**\u003cbr\u003e\nGetResultMap returns a result map which contains the values for only the\nprovided keys.\n\n**`Delete(context.Context, Key) bool`**\u003cbr\u003e\nDelete removes the value for the provided key and returns true if successful.\n\n**`ClearAll(context.Context) bool`**\u003cbr\u003e\nClearAll removes all values from the cache and returns true if successfully\ncleared\n\n#### Tracer\n\n\u003e Tracer provides an interface used by the DataLoader for tracing requests\n\u003e through the application. Tracing occurs on calls to `Load`, `LoadMany` and\n\u003e when the `BatchFunction` gets called.\n\n**`NewNoOpTracer() Tracer`**\u003cbr\u003e\nNewNoOpTracer returns an instance of a blank tracer that does not output anything.\n\n**`NewOpenTracingTracer() Tracer`**\u003cbr\u003e\nNewOpenTracingTracer returns an instance of a tracer which conforms to the open\ntracing standard.\n\n**`Load(context.Context, Key) (context.Context, LoadFinishFunc)`**\u003cbr\u003e\nLoad performs tracing around calls to the `Load` function. It returns a context\nwith tracing information and a finish function which ends the tracing.\n\n**`LoadMany(context.Context, Key) (context.Context, LoadManyFinishFunc)`**\u003cbr\u003e\nLoadMany performs tracing around calls to the `LoadMany` function. It returns a\ncontext with tracing information and a finish function which ends the tracing.\n\n**`Batch(context.Context) (context.Context, BatchFinishFunc)`**\u003cbr\u003e\nBatch performs tracing around calls to the `Batch` function. It returns a context\nwith the tracing information attached to it and a finish function which ends the\ntracing.\n\n**`LoadFinishFunc(Result)`**\u003cbr\u003e\nLoadFinishFunc ends tracing started by `Load` and gets passed the resolved\nresult for the queried key.\n\n**`LoadManyFinishFunc(ResultMap)`**\u003cbr\u003e\nLoadFinishFunc ends tracing started by `LoadMany` and gets passed the resolved\nresult map for the queried keys.\n\n**`BatchFinishFunc(ResultMap)`**\u003cbr\u003e\nBatchFinishFunc ends tracing started by `Batch` and gets passed the resolved\nresult map for the key or keys.\n\n#### Counter\n\n\u003e Counter provides an interface used to atomically count and track a value and\n\u003e to track if it's reached a certain set count. Internally it is used by\n\u003e strategies to track the number of calls to `Load` and `LoadMany`.\n\n**`NewCounter(int) Counter`**\u003cbr\u003e\nNewCounter returns a new instance of a counter for the provided capacity. Once\nthe capacity is reached, the counter is considered complete.\n\n**`Increment() bool`**\u003cbr\u003e\nIncrement increases the counter by one and returns true if the counter has hit\nit's capacity.\n\n**`ResetCounter()`**\u003cbr\u003e\nResetCounter sets the counter back to 0 but keeps the original capacity.\n\n## Strategies\n\nBoth the `Standard` and `Sozu` strategies allow for concurrent operations before\nexecuting the returned Thunk function and both ensure that other callers are not\nblocked when waiting for data to be read. This allows certain resolvers that use\na shared strategy to block some of the time while allowing other resolvers to\nreceive their data and continue their operations.\n\n### Standard\n\nThe standard strategy initially calls the batch function when one of two\nconditions are met:\n\n1.  The number of calls the Load or LoadMany equals the capacity of the loader\n2.  The timeout set has been reached (default to 16 milliseconds)\n\nSubsequent calls to `Load()` or `LoadMany()` will execute the batch function for\neach caller.\n\nThe standard strategy is useful for instance when _n+1_ calls are lower than the\ncapacity. For instance, it could be known that the app will need to resolve at\nleast 10 keys, but if there are more it is significantly less than 10 more (e.g.\n2 more calls for a total of 12).\n\n### Sozu\n\nThe sozu strategy initially calls the batch function when one of two conditions\nare met:\n\n1.  The number of calls the Load or LoadMany equals the capacity of the loader\n2.  The timeout set has been reached (default to 16 milliseconds)\n\nSubsequent calls to `Load()` or `LoadMany()` will call the batch function when\none of two conditions are met:\n\n1.  The number of calls the Load or LoadMany equals the capacity of the loader\n2.  The timeout set has been reached (default to 16 milliseconds)\n\nThe sozu strategy is useful for instance when known calls to load will be called\nin a defined and equal group. For instance, the initial call can be to resolve\n10 keys. Then it should be known that if there are more keys, that there will be\n10 more keys resulting in the batch function being called twice, each time with\n10 keys.\n\n### Once\n\nThe once strategy initially calls the batch function under one of two\nconditions:\n\n1.  If `InBackground` option is false, it executes the batch function then Thunk\n    is invoked\n2.  If `InBackground` option is true, it executes the batch function in a\n    background go routine. Calls to the Thunk function will block until the go\n    routine completes.\n\nThe once strategy is useful when wanting to execute a batch function a single\ntime either directly, and keeping a consistent application api aligning with the\nother functions. Or when wanting to call the batch function in the background\nwhile performing some other time consuming operation and not wanting to handle\nthe actual go routine.\n\n## TODO\n\n- [x] Set a max duration that a call to `Load(Key)` can block. Start from the\n      initial call to `Load(Key)`.\n- [x] Caching approach/interface\n- [x] Tests!!\n- [x] LoadMany - ability to call load with multiple keys\n- [ ] Examples\n- [x] Logging\n- [x] Tracing\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandy9775%2Fdataloader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandy9775%2Fdataloader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandy9775%2Fdataloader/lists"}