micw/server/core/blocks_commiter.go

88 lines
2.1 KiB
Go
Raw Normal View History

2024-11-17 21:24:19 +00:00
package core
import (
"context"
"github.com/matchsystems/werr"
"mic-wallet/server/repository"
"sync"
"time"
)
type CommitBlockFunc func(ctx context.Context, block *Block) error
type IBlocksCommiter interface {
// CommitBlock pushes blocks into the storage (db).
// Block must be not null
CommitBlock(ctx context.Context, block *Block)
}
type BlocksCommiter struct {
BlocksQueue []*Block
Lock *sync.RWMutex
2024-11-24 14:04:52 +00:00
CountLim int
CountBusy int
2024-11-17 21:24:19 +00:00
TaskDeadline time.Duration
ErrChan chan error
CommitBlockFunc CommitBlockFunc
}
func NewBlocksCommiter(repo *repository.Repository, errChan chan error, workersCountLim int) *BlocksCommiter {
return &BlocksCommiter{
BlocksQueue: make([]*Block, 0),
Lock: &sync.RWMutex{},
2024-11-24 14:04:52 +00:00
CountLim: workersCountLim,
2024-11-17 21:24:19 +00:00
CountBusy: 0,
TaskDeadline: 0,
2024-11-24 14:04:52 +00:00
ErrChan: errChan,
2024-11-17 21:24:19 +00:00
CommitBlockFunc: func(ctx context.Context, block *Block) error {
return CommitBlock(ctx, repo, block)
},
}
}
2024-11-24 14:04:52 +00:00
// execute executes commiting block with deadline and cancelable by the context
// provided when calling CommitBlock.
// all the errors from it (including context) are sent to BlocksCommiter.ErrChan
2024-11-17 21:24:19 +00:00
func (bc *BlocksCommiter) execute(ctx context.Context, block *Block) {
if bc.TaskDeadline > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, bc.TaskDeadline)
defer cancel()
}
errChan := make(chan error)
go func() {
if err := bc.CommitBlockFunc(ctx, block); err != nil {
errChan <- err
}
}()
select {
case <-ctx.Done():
2024-11-24 14:04:52 +00:00
bc.ErrChan <- werr.Wrapf(ctx.Err(), "ctx error while commiting block %d", block.Id)
2024-11-17 21:24:19 +00:00
case err := <-errChan:
2024-11-24 14:04:52 +00:00
bc.ErrChan <- werr.Wrapf(err, "error while commiting block %d", block.Id)
2024-11-17 21:24:19 +00:00
}
}
func (bc *BlocksCommiter) CommitBlock(ctx context.Context, block *Block) {
bc.Lock.Lock()
if bc.CountBusy >= bc.CountLim {
bc.BlocksQueue = append(bc.BlocksQueue, block)
return
}
bc.CountBusy++
bc.Lock.Unlock()
for {
bc.execute(ctx, block)
bc.Lock.Lock()
if len(bc.BlocksQueue) < 1 {
bc.CountBusy--
bc.Lock.Unlock()
return
}
block = bc.BlocksQueue[0]
bc.BlocksQueue = bc.BlocksQueue[1:]
bc.Lock.Unlock()
}
}