sqldb: rename ExecutePagedQuery to ExecuteBatchQuery
We rename this helper along the config types & helper types for it because the word "page" is used more often in the context of paging through results using an offset and limit whereas this helper is specifically used to split up the slice in queries of the form "WHERE x in []slice". We do this rename so that there is mimimal confusion in contexts where we use batching along with actual paging. The config struct is also renamed to QueryConfig in preparation for it holding more config options.
This commit is contained in:
8 files changed
+105
-95
No files matched your search
+34
-24
@@ -5,46 +5,56 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// PagedQueryFunc represents a function that takes a slice of converted items
|
||||
// QueryConfig holds configuration values for SQL queries.
|
||||
type QueryConfig struct {
|
||||
// MaxBatchSize is the maximum number of items included in a batch
|
||||
// query IN clauses list.
|
||||
MaxBatchSize int
|
||||
}
|
||||
|
||||
// DefaultQueryConfig returns a default configuration for SQL queries.
|
||||
func DefaultQueryConfig() *QueryConfig {
|
||||
return &QueryConfig{
|
||||
// TODO(elle): make configurable & have different defaults
|
||||
// for SQLite and Postgres.
|
||||
MaxBatchSize: 250,
|
||||
}
|
||||
}
|
||||
|
||||
// BatchQueryFunc represents a function that takes a batch of converted items
|
||||
// and returns results.
|
||||
type PagedQueryFunc[T any, R any] func(context.Context, []T) ([]R, error)
|
||||
type BatchQueryFunc[T any, R any] func(context.Context, []T) ([]R, error)
|
||||
|
||||
// ItemCallbackFunc represents a function that processes individual results.
|
||||
type ItemCallbackFunc[R any] func(context.Context, R) error
|
||||
|
||||
// ConvertFunc represents a function that converts from input type to query type
|
||||
// for the batch query.
|
||||
type ConvertFunc[I any, T any] func(I) T
|
||||
|
||||
// PagedQueryConfig holds configuration values for calls to ExecutePagedQuery.
|
||||
type PagedQueryConfig struct {
|
||||
PageSize int
|
||||
}
|
||||
|
||||
// DefaultPagedQueryConfig returns a default configuration
|
||||
func DefaultPagedQueryConfig() *PagedQueryConfig {
|
||||
return &PagedQueryConfig{
|
||||
// TODO(elle): make configurable & have different defaults
|
||||
// for SQLite and Postgres.
|
||||
PageSize: 250,
|
||||
}
|
||||
}
|
||||
|
||||
// ExecutePagedQuery executes a paginated query over a slice of input items.
|
||||
// ExecuteBatchQuery executes a query in batches over a slice of input items.
|
||||
// It converts the input items to a query type using the provided convertFunc,
|
||||
// executes the query using the provided queryFunc, and applies the callback
|
||||
// to each result.
|
||||
func ExecutePagedQuery[I any, T any, R any](ctx context.Context,
|
||||
cfg *PagedQueryConfig, inputItems []I, convertFunc ConvertFunc[I, T],
|
||||
queryFunc PagedQueryFunc[T, R], callback ItemCallbackFunc[R]) error {
|
||||
// executes the query in batches using the provided queryFunc, and applies
|
||||
// the callback to each result. This is useful for queries using the
|
||||
// "WHERE x IN []slice" pattern. It takes that slice, splits it into batches of
|
||||
// size MaxBatchSize, and executes the query for each batch.
|
||||
//
|
||||
// NOTE: it is the caller's responsibility to ensure that the expected return
|
||||
// results are unique across all pages. Meaning that if the input items are
|
||||
// split up, a result that is returned in one page should not be expected to
|
||||
// be returned in another page.
|
||||
func ExecuteBatchQuery[I any, T any, R any](ctx context.Context,
|
||||
cfg *QueryConfig, inputItems []I, convertFunc ConvertFunc[I, T],
|
||||
queryFunc BatchQueryFunc[T, R], callback ItemCallbackFunc[R]) error {
|
||||
|
||||
if len(inputItems) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Process items in pages.
|
||||
for i := 0; i < len(inputItems); i += cfg.PageSize {
|
||||
for i := 0; i < len(inputItems); i += cfg.MaxBatchSize {
|
||||
// Calculate the end index for this page.
|
||||
end := i + cfg.PageSize
|
||||
end := i + cfg.MaxBatchSize
|
||||
if end > len(inputItems) {
|
||||
end = len(inputItems)
|
||||
}
|
||||
|
||||
+21
-21
@@ -12,17 +12,17 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestExecutePagedQuery tests the ExecutePagedQuery function which processes
|
||||
// TestExecuteBatchQuery tests the ExecuteBatchQuery function which processes
|
||||
// items in pages, allowing for efficient querying and processing of large
|
||||
// datasets.
|
||||
func TestExecutePagedQuery(t *testing.T) {
|
||||
func TestExecuteBatchQuery(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("empty input returns nil", func(t *testing.T) {
|
||||
var (
|
||||
cfg = DefaultPagedQueryConfig()
|
||||
cfg = DefaultQueryConfig()
|
||||
inputItems []int
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ func TestExecutePagedQuery(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := ExecutePagedQuery(
|
||||
err := ExecuteBatchQuery(
|
||||
ctx, cfg, inputItems, convertFunc, queryFunc, callback,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
@@ -55,8 +55,8 @@ func TestExecutePagedQuery(t *testing.T) {
|
||||
convertedItems []string
|
||||
callbackResults []string
|
||||
inputItems = []int{1, 2, 3, 4, 5}
|
||||
cfg = &PagedQueryConfig{
|
||||
PageSize: 10,
|
||||
cfg = &QueryConfig{
|
||||
MaxBatchSize: 10,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -81,7 +81,7 @@ func TestExecutePagedQuery(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := ExecutePagedQuery(
|
||||
err := ExecuteBatchQuery(
|
||||
ctx, cfg, inputItems, convertFunc, queryFunc, callback,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
@@ -104,8 +104,8 @@ func TestExecutePagedQuery(t *testing.T) {
|
||||
pageSizes []int
|
||||
allResults []string
|
||||
inputItems = []int{1, 2, 3, 4, 5, 6, 7, 8}
|
||||
cfg = &PagedQueryConfig{
|
||||
PageSize: 3,
|
||||
cfg = &QueryConfig{
|
||||
MaxBatchSize: 3,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -131,7 +131,7 @@ func TestExecutePagedQuery(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := ExecutePagedQuery(
|
||||
err := ExecuteBatchQuery(
|
||||
ctx, cfg, inputItems, convertFunc, queryFunc, callback,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
@@ -144,7 +144,7 @@ func TestExecutePagedQuery(t *testing.T) {
|
||||
|
||||
t.Run("query function error is propagated", func(t *testing.T) {
|
||||
var (
|
||||
cfg = DefaultPagedQueryConfig()
|
||||
cfg = DefaultQueryConfig()
|
||||
inputItems = []int{1, 2, 3}
|
||||
)
|
||||
|
||||
@@ -165,7 +165,7 @@ func TestExecutePagedQuery(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := ExecutePagedQuery(
|
||||
err := ExecuteBatchQuery(
|
||||
ctx, cfg, inputItems, convertFunc, queryFunc, callback,
|
||||
)
|
||||
require.ErrorContains(t, err, "query failed for page "+
|
||||
@@ -174,7 +174,7 @@ func TestExecutePagedQuery(t *testing.T) {
|
||||
|
||||
t.Run("callback error is propagated", func(t *testing.T) {
|
||||
var (
|
||||
cfg = DefaultPagedQueryConfig()
|
||||
cfg = DefaultQueryConfig()
|
||||
inputItems = []int{1, 2, 3}
|
||||
)
|
||||
|
||||
@@ -195,7 +195,7 @@ func TestExecutePagedQuery(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := ExecutePagedQuery(
|
||||
err := ExecuteBatchQuery(
|
||||
ctx, cfg, inputItems, convertFunc, queryFunc, callback,
|
||||
)
|
||||
require.ErrorContains(t, err, "callback failed for result: "+
|
||||
@@ -205,8 +205,8 @@ func TestExecutePagedQuery(t *testing.T) {
|
||||
t.Run("query error in second page is propagated", func(t *testing.T) {
|
||||
var (
|
||||
inputItems = []int{1, 2, 3, 4}
|
||||
cfg = &PagedQueryConfig{
|
||||
PageSize: 2,
|
||||
cfg = &QueryConfig{
|
||||
MaxBatchSize: 2,
|
||||
}
|
||||
queryCallCount int
|
||||
)
|
||||
@@ -230,7 +230,7 @@ func TestExecutePagedQuery(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := ExecutePagedQuery(
|
||||
err := ExecuteBatchQuery(
|
||||
ctx, cfg, inputItems, convertFunc, queryFunc, callback,
|
||||
)
|
||||
require.ErrorContains(t, err, "query failed for page "+
|
||||
@@ -238,10 +238,10 @@ func TestExecutePagedQuery(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// TestSQLSliceQueries tests ExecutePageQuery helper by first showing that a
|
||||
// TestSQLSliceQueries tests ExecuteBatchQuery helper by first showing that a
|
||||
// query the /*SLICE:<field_name>*/ directive has a maximum number of
|
||||
// parameters it can handle, and then showing that the paginated version which
|
||||
// uses ExecutePagedQuery instead of a raw query can handle more parameters by
|
||||
// uses ExecuteBatchQuery instead of a raw query can handle more parameters by
|
||||
// executing the query in pages.
|
||||
func TestSQLSliceQueries(t *testing.T) {
|
||||
t.Parallel()
|
||||
@@ -299,9 +299,9 @@ func TestSQLSliceQueries(t *testing.T) {
|
||||
return db.GetChannelsByOutpoints(ctx, pageOutpoints)
|
||||
}
|
||||
|
||||
err := ExecutePagedQuery(
|
||||
err := ExecuteBatchQuery(
|
||||
ctx,
|
||||
DefaultPagedQueryConfig(),
|
||||
DefaultQueryConfig(),
|
||||
queryParams,
|
||||
func(s string) string {
|
||||
return s
|
||||
|
||||
Reference in new issue
Block a user