Description
A problem I've run into a fair amount is dealing with APIs which only accept a maximum number of inputs at once, though I may have more than that number of inputs that I would like to ultimately process.
For example, Amazon S3 can only delete up to 1000 objects in a single DeleteObjects
API request (https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html). I've run into similar issues in the past when injecting a large number of routes into the Linux kernel via route netlink, or when modifying a large number of WireGuard peers at once.
To deal with this situation in a generic way, I've come up with the following:
package slices
// Chunk batches []T into [][]T in groups of size. The final chunk of []T will be
// smaller than size if the input slice cannot be chunked evenly. It does not
// make any copies of slice elements.
//
// As an example, take a slice of 5 integers and create chunks of 2 integers
// each (the final value creates a short chunk):
// slices.Chunk([]int{1, 2, 3, 4, 5}, 2) = [][]int{{1, 2}, {3, 4}, {5}}
func Chunk[T any](slice []T, size int) [][]T {
var chunks [][]T
for i := 0; i < len(slice); {
// Clamp the last chunk to the slice bound as necessary.
end := size
if l := len(slice[i:]); l < size {
end = l
}
// Set the capacity of each chunk so that appending to a chunk does not
// modify the original slice.
chunks = append(chunks, slice[i:i+end:i+end])
i += end
}
return chunks
}
Which is then used as follows:
objs, err := c.allObjects(ctx)
if err != nil {
return nil, err
}
// Begin deleting the objects concurrently in batches of 1000 (the
// maximum limit size supported by S3).
const limit = 1000
eg, ctx := errgroup.WithContext(ctx)
eg.SetLimit(10)
for _, chunk := range slices.Chunk(objs, limit) {
chunk := chunk
eg.Go(func() error { /* deletion logic */ })
}
If this proposal is accepted, I'd be happy to send a CL for the above. Thanks for your time.
Edit 1: updated second parameter name to size int
as inspired by Rust's chunks
method: https://doc.rust-lang.org/std/primitive.slice.html#method.chunks.
Edit 2: set capacity for each chunk per next comment.