feat(storage): rpc driver client pooling and memory-constrained cache
All checks were successful
arcad/edge/pipeline/head This commit looks good
All checks were successful
arcad/edge/pipeline/head This commit looks good
driver ref #20
This commit is contained in:
@ -5,7 +5,6 @@ import (
|
||||
"net/url"
|
||||
|
||||
"github.com/keegancsmith/rpc"
|
||||
"gitlab.com/wpetit/goweb/logger"
|
||||
|
||||
"forge.cadoles.com/arcad/edge/pkg/storage"
|
||||
"forge.cadoles.com/arcad/edge/pkg/storage/driver/rpc/server/blob"
|
||||
@ -13,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
type BlobStore struct {
|
||||
serverURL *url.URL
|
||||
withClient WithClientFunc
|
||||
}
|
||||
|
||||
// DeleteBucket implements storage.BlobStore.
|
||||
@ -75,27 +74,11 @@ func (s *BlobStore) call(ctx context.Context, serviceMethod string, args any, re
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *BlobStore) withClient(ctx context.Context, fn func(ctx context.Context, client *rpc.Client) error) error {
|
||||
client, err := rpc.DialHTTPPath("tcp", s.serverURL.Host, s.serverURL.Path+"?"+s.serverURL.RawQuery)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := client.Close(); err != nil {
|
||||
logger.Error(ctx, "could not close rpc client", logger.CapturedE(errors.WithStack(err)))
|
||||
}
|
||||
}()
|
||||
|
||||
if err := fn(ctx, client); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewBlobStore(serverURL *url.URL) *BlobStore {
|
||||
return &BlobStore{serverURL}
|
||||
withClient := WithPooledClient(serverURL)
|
||||
return &BlobStore{
|
||||
withClient: withClient,
|
||||
}
|
||||
}
|
||||
|
||||
var _ storage.BlobStore = &BlobStore{}
|
||||
|
94
pkg/storage/driver/rpc/client/client_pool.go
Normal file
94
pkg/storage/driver/rpc/client/client_pool.go
Normal file
@ -0,0 +1,94 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/jackc/puddle/v2"
|
||||
"github.com/keegancsmith/rpc"
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/logger"
|
||||
)
|
||||
|
||||
func NewClientPool(serverURL *url.URL, poolSize int) (*puddle.Pool[*rpc.Client], error) {
|
||||
constructor := func(context.Context) (*rpc.Client, error) {
|
||||
client, err := rpc.DialHTTPPath("tcp", serverURL.Host, serverURL.Path+"?"+serverURL.RawQuery)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
destructor := func(client *rpc.Client) {
|
||||
if err := client.Close(); err != nil {
|
||||
logger.Error(context.Background(), "could not close client", logger.CapturedE(errors.WithStack(err)))
|
||||
}
|
||||
}
|
||||
|
||||
maxPoolSize := int32(poolSize)
|
||||
|
||||
pool, err := puddle.NewPool(&puddle.Config[*rpc.Client]{Constructor: constructor, Destructor: destructor, MaxSize: maxPoolSize})
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
type WithClientFunc func(ctx context.Context, fn func(ctx context.Context, client *rpc.Client) error) error
|
||||
|
||||
func WithPooledClient(serverURL *url.URL) WithClientFunc {
|
||||
var (
|
||||
pool *puddle.Pool[*rpc.Client]
|
||||
createPool sync.Once
|
||||
)
|
||||
|
||||
return func(ctx context.Context, fn func(context.Context, *rpc.Client) error) error {
|
||||
var err error
|
||||
createPool.Do(func() {
|
||||
rawPoolSize := serverURL.Query().Get("clientPoolSize")
|
||||
if rawPoolSize == "" {
|
||||
rawPoolSize = "5"
|
||||
}
|
||||
|
||||
var poolSize int64
|
||||
|
||||
poolSize, err = strconv.ParseInt(rawPoolSize, 10, 32)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "could not parse clientPoolSize url query parameter")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
pool, err = NewClientPool(serverURL, int(poolSize))
|
||||
if err != nil {
|
||||
err = errors.WithStack(err)
|
||||
|
||||
return
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
clientResource, err := pool.Acquire(ctx)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
if err := fn(ctx, clientResource.Value()); err != nil {
|
||||
if errors.Is(err, rpc.ErrShutdown) {
|
||||
clientResource.Destroy()
|
||||
}
|
||||
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
clientResource.Release()
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/keegancsmith/rpc"
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/logger"
|
||||
|
||||
"forge.cadoles.com/arcad/edge/pkg/storage"
|
||||
"forge.cadoles.com/arcad/edge/pkg/storage/driver/rpc/server/document"
|
||||
@ -14,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
type DocumentStore struct {
|
||||
serverURL *url.URL
|
||||
withClient WithClientFunc
|
||||
}
|
||||
|
||||
// Delete implements storage.DocumentStore.
|
||||
@ -108,27 +107,12 @@ func (s *DocumentStore) call(ctx context.Context, serviceMethod string, args any
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DocumentStore) withClient(ctx context.Context, fn func(ctx context.Context, client *rpc.Client) error) error {
|
||||
client, err := rpc.DialHTTPPath("tcp", s.serverURL.Host, s.serverURL.Path+"?"+s.serverURL.RawQuery)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
func NewDocumentStore(serverURL *url.URL) *DocumentStore {
|
||||
withClient := WithPooledClient(serverURL)
|
||||
|
||||
return &DocumentStore{
|
||||
withClient: withClient,
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := client.Close(); err != nil {
|
||||
logger.Error(ctx, "could not close rpc client", logger.CapturedE(errors.WithStack(err)))
|
||||
}
|
||||
}()
|
||||
|
||||
if err := fn(ctx, client); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewDocumentStore(url *url.URL) *DocumentStore {
|
||||
return &DocumentStore{url}
|
||||
}
|
||||
|
||||
var _ storage.DocumentStore = &DocumentStore{}
|
||||
|
Reference in New Issue
Block a user