func (b batcher) SendBatch(ctx context.Context, conn pgxconn.PgxConn) (err error) {
	batch := make([]batchItem, len(b.batch))
	i := 0
	for q := range b.batch {
		id, ok := b.cache.Get(q)
		if !ok {
			batch[i] = q
			i++
			continue
		}
		b.batch[q] = id
	}
	if i == 0 {  
		return nil
	}
	batch = batch[:i]
	sort.Sort(sortableItems(batch))
	dbBatch := conn.NewBatch()
	for _, item := range batch {
		item.AddToDBBatch(dbBatch)
	}
	br, err := conn.SendBatch(ctx, dbBatch)
	if err != nil {
		return err
	}
	defer func() {
		if tempErr := br.Close(); err == nil {
			err = tempErr
		}
	}()
	for _, item := range batch {
		id, err := item.ScanIDs(br)
		if err != nil {
			return err
		}
		b.batch[item] = id
		b.cache.Insert(item, id, item.SizeInCache())
	}
	return nil
}
SendBatch sends the batch over the DB connections and gets the ID results.
Caching is also checked for existance of IDs before sending to the DB to
avoid unnecessary IO.
package trace
import (
	"context"
	"fmt"
	"sort"
	"github.com/jackc/pgtype"
	"github.com/jackc/pgx/v4"
	"github.com/timescale/promscale/pkg/pgmodel/common/errors"
	"github.com/timescale/promscale/pkg/pgxconn"
)
type cache interface {
	Get(key interface{}) (interface{}, bool)
	Insert(key interface{}, value interface{}, size uint64) (interface{}, bool)
}
type sortable interface {
	Before(sortable) bool
}
type cacheable interface {
	SizeInCache() uint64
}
type batchItem interface {
	sortable
	cacheable
	AddToDBBatch(pgxconn.PgxBatch)
	ScanIDs(pgx.BatchResults) (interface{}, error)
}
type sortableItems []batchItem
func (q sortableItems) Len() int {
	return len(q)
}
func (q sortableItems) Less(i, j int) bool {
	return q[i].Before(q[j])
}
func (q sortableItems) Swap(i, j int) {
	q[i], q[j] = q[j], q[i]
}
type batcher struct {
	batch map[batchItem]interface{}
	cache cache
}
func newBatcher(cache cache) batcher {
	return batcher{
		batch: make(map[batchItem]interface{}),
		cache: cache,
	}
}
func (b batcher) Queue(i batchItem) {
	b.batch[i] = nil
}