func (s *Store) EnsureBlob(ctx context.Context, tID *tenant.ID, data io.Reader) (Hash, error) {
	u := uuid.New()
	h := sha256.New()
	if _, err := s.InsertToRope(ctx, tID, u, data, h, 0); err != nil {
		return Hash{}, err
	}
	var ret Hash
	h.Sum(ret[:0])
	return ret, s.CommitRope(ctx, tID, u, ret)
}
EnsureBlob will insert the contents provided by the given reader into
the database, returning a content hash.

This method is idempotent.
package blob
import (
	"bytes"
	"context"
	"crypto/sha256"
	"hash"
	"io"
	"math"
	"os"
	"time"
	"github.com/Mandala/go-log"
	"github.com/bobvawter/cacheroach/api/tenant"
	"github.com/bobvawter/cacheroach/pkg/cache"
	"github.com/bobvawter/cacheroach/pkg/store/config"
	"github.com/bobvawter/cacheroach/pkg/store/util"
	"github.com/google/uuid"
	"github.com/google/wire"
	"github.com/jackc/pgconn"
	"github.com/jackc/pgx/v4/pgxpool"
	"github.com/pkg/errors"
	"golang.org/x/sync/errgroup"
	"golang.org/x/sync/semaphore"
)
var Set = wire.NewSet(ProvideStore)
const (
	blobMetaKey  = "meta:"
	chunkDataKey = "chunk:"
)
type Store struct {
	cache  *cache.Cache
	config *config.Config
	db     *pgxpool.Pool
	logger *log.Logger
	sem    *semaphore.Weighted
}
func ProvideStore(
	ctx context.Context,
	cache *cache.Cache,
	config *config.Config,
	db *pgxpool.Pool,
	logger *log.Logger,
) (*Store, func()) {
	ctx, cancel := context.WithCancel(ctx)
	s := &Store{
		cache:  cache,
		config: config,
		db:     db,
		logger: logger,
		sem:    semaphore.NewWeighted(int64(config.ChunkConcurrency)),
	}
	go s.backoffLoop(ctx)
	return s, cancel
}
func (s *Store) CommitRope(ctx context.Context, tID *tenant.ID, ropeID uuid.UUID, hash Hash) error {
	return util.Retry(ctx, func(ctx context.Context) error {
		_, err := s.db.Exec(ctx,
			"UPDATE ropes SET hash = $1 where rope = $2 AND tenant = $3",
			hash[:], ropeID, tID)
		if pgErr, ok := err.(*pgconn.PgError); ok {
			if pgErr.Code == "23505" {
				_, err = s.db.Exec(ctx,
					"DELETE FROM ropes where rope = $1 AND tenant = $2",
					ropeID, tID)
			}
		}
		return err
	})
}