Files
libsam/pkg/shreder/client.go

133 lines
2.7 KiB
Go
Raw Normal View History

2025-12-26 11:34:45 +08:00
package shreder
2025-12-26 10:57:37 +08:00
import (
"context"
2026-01-05 12:45:32 +08:00
"fmt"
2026-01-07 21:15:54 +08:00
"runtime"
"time"
2025-12-26 10:57:37 +08:00
2026-01-05 12:45:32 +08:00
"github.com/gagliardetto/solana-go/rpc"
2026-01-07 21:15:54 +08:00
"github.com/panjf2000/ants/v2"
2025-12-26 10:57:37 +08:00
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
2025-12-30 11:03:11 +08:00
type Client struct {
2026-01-07 21:15:54 +08:00
EnableClientStats bool
conn *grpc.ClientConn
client ShrederServiceClient
tableLoader *AddressTables
subscription map[string]*SubscribeRequestFilterTransactions
pool *ants.Pool
lastSlot uint64
lastSlotTime time.Time
2025-12-26 10:57:37 +08:00
}
2025-12-26 11:34:45 +08:00
func NewShrederClient(
url string,
2026-01-05 12:45:32 +08:00
rpcClient *rpc.Client,
2025-12-30 11:03:11 +08:00
subscription map[string]*SubscribeRequestFilterTransactions,
) (*Client, func(), error) {
2026-01-05 12:45:32 +08:00
if rpcClient == nil {
return nil, func() {}, fmt.Errorf("rpc client is nil")
}
2025-12-26 10:57:37 +08:00
conn, err := grpc.NewClient(url, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, func() {}, err
}
2026-01-07 21:15:54 +08:00
poolSize := runtime.NumCPU()*2 + 2
logger.Info("creating shreder client", "url", url, "pool_size", poolSize)
pool, err := ants.NewPool(poolSize, ants.WithNonblocking(false))
if err != nil {
return nil, func() {}, err
}
2025-12-30 11:03:11 +08:00
s := &Client{
2025-12-26 11:34:45 +08:00
conn: conn,
2025-12-30 11:03:11 +08:00
client: NewShrederServiceClient(conn),
2025-12-26 11:34:45 +08:00
subscription: subscription,
2026-01-05 12:45:32 +08:00
tableLoader: NewAddressTables(rpcClient),
2026-01-07 21:15:54 +08:00
pool: pool,
2025-12-26 10:57:37 +08:00
}
return s, func() {
s.Wait()
}, nil
}
2025-12-30 11:03:11 +08:00
func (c *Client) Wait() {
2026-01-05 12:45:32 +08:00
logger.Debug("waiting for shreder client to stop")
2025-12-26 10:57:37 +08:00
2026-01-07 21:15:54 +08:00
if c.pool != nil {
c.pool.Release()
}
2025-12-26 10:57:37 +08:00
err := c.conn.Close()
if err != nil {
2026-01-05 12:45:32 +08:00
logger.Error("failed to close connection: ", "err", err)
2025-12-26 10:57:37 +08:00
}
2026-01-05 12:45:32 +08:00
logger.Debug("shreder client stopped")
2025-12-26 10:57:37 +08:00
}
2025-12-30 11:03:11 +08:00
func (c *Client) ReadSync(ctx context.Context, txCh chan<- TxSignalBatch) error {
2025-12-26 10:57:37 +08:00
stream, err := c.client.SubscribeTransactions(ctx)
if err != nil {
return err
}
2026-01-07 21:15:54 +08:00
logger.Debug("subscribing to transactions")
2025-12-30 11:03:11 +08:00
err = stream.Send(&SubscribeTransactionsRequest{
2025-12-26 11:34:45 +08:00
Transactions: c.subscription,
2025-12-26 10:57:37 +08:00
})
if err != nil {
return err
}
for {
response, err := stream.Recv()
if err != nil {
return err
}
2026-01-07 21:15:54 +08:00
if c.EnableClientStats {
slot := response.Transaction.Slot
now := time.Now()
if c.lastSlotTime.IsZero() || slot > c.lastSlot {
if !c.lastSlotTime.IsZero() {
logger.Info("block processed", "running", c.pool.Running(), "slot", slot, "prev_slot", c.lastSlot, "delta_ms", now.Sub(c.lastSlotTime).Milliseconds())
}
c.lastSlot = slot
c.lastSlotTime = now
}
2025-12-26 10:57:37 +08:00
}
2026-01-07 21:15:54 +08:00
txData := response.Transaction
err = c.pool.Submit(func() {
txBatch := ParseTransaction(txData, c.tableLoader, c.EnableClientStats)
if len(txBatch) == 0 {
return
}
2025-12-26 10:57:37 +08:00
2026-01-07 21:15:54 +08:00
for _, tx := range txBatch {
tx.Source = "shreder"
}
select {
case <-ctx.Done():
return
case txCh <- txBatch:
}
})
if err != nil {
return err
2025-12-26 10:57:37 +08:00
}
}
}