2PC released
This commit is contained in:
216
internal/cluster/worker_pool.go
Normal file
216
internal/cluster/worker_pool.go
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright 2026 Safronov Grigorii
|
||||
*
|
||||
* Licensed under the CDDL, Version 1.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* You may obtain a copy of the License at
|
||||
* https://opensource.org/licenses/CDDL-1.0
|
||||
*/
|
||||
|
||||
// Файл: internal/cluster/worker_pool.go
|
||||
// Назначение: Пул воркеров для ограничения количества горутин
|
||||
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LoggerInterface определяет интерфейс для логирования
|
||||
type LoggerInterface interface {
|
||||
Debug(msg string)
|
||||
Info(msg string)
|
||||
Error(msg string)
|
||||
Warn(msg string)
|
||||
}
|
||||
|
||||
// WorkerTask представляет задачу для выполнения в пуле
|
||||
type WorkerTask struct {
|
||||
ID string
|
||||
Handler func() error
|
||||
CreatedAt int64
|
||||
}
|
||||
|
||||
// WorkerPool управляет пулом воркеров
|
||||
type WorkerPool struct {
|
||||
maxWorkers int
|
||||
taskQueue chan *WorkerTask
|
||||
wg sync.WaitGroup
|
||||
stopChan chan struct{}
|
||||
activeTasks atomic.Int32
|
||||
totalTasks atomic.Uint64
|
||||
failedTasks atomic.Uint64
|
||||
logger LoggerInterface
|
||||
panicHandler func(interface{})
|
||||
}
|
||||
|
||||
// NewWorkerPool создаёт новый пул воркеров
|
||||
func NewWorkerPool(maxWorkers int, logger LoggerInterface) *WorkerPool {
|
||||
if maxWorkers <= 0 {
|
||||
maxWorkers = 100 // ограничение по умолчанию
|
||||
}
|
||||
|
||||
pool := &WorkerPool{
|
||||
maxWorkers: maxWorkers,
|
||||
taskQueue: make(chan *WorkerTask, maxWorkers*10),
|
||||
stopChan: make(chan struct{}),
|
||||
logger: logger,
|
||||
panicHandler: defaultPanicHandler,
|
||||
}
|
||||
|
||||
for i := 0; i < maxWorkers; i++ {
|
||||
pool.wg.Add(1)
|
||||
go pool.worker(i)
|
||||
}
|
||||
|
||||
return pool
|
||||
}
|
||||
|
||||
// defaultPanicHandler обрабатывает панику в горутине
|
||||
func defaultPanicHandler(r interface{}) {
|
||||
fmt.Printf("PANIC recovered: %v\n%s\n", r, debug.Stack())
|
||||
}
|
||||
|
||||
// SetPanicHandler устанавливает обработчик паник
|
||||
func (wp *WorkerPool) SetPanicHandler(handler func(interface{})) {
|
||||
if handler != nil {
|
||||
wp.panicHandler = handler
|
||||
}
|
||||
}
|
||||
|
||||
// worker запускает воркер
|
||||
func (wp *WorkerPool) worker(id int) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if wp.panicHandler != nil {
|
||||
wp.panicHandler(r)
|
||||
}
|
||||
if wp.logger != nil {
|
||||
wp.logger.Error(fmt.Sprintf("Worker %d panicked: %v", id, r))
|
||||
}
|
||||
// Перезапускаем воркер
|
||||
wp.wg.Add(1)
|
||||
go wp.worker(id)
|
||||
}
|
||||
wp.wg.Done()
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-wp.stopChan:
|
||||
return
|
||||
case task, ok := <-wp.taskQueue:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
wp.activeTasks.Add(1)
|
||||
wp.totalTasks.Add(1)
|
||||
|
||||
func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
wp.failedTasks.Add(1)
|
||||
if wp.panicHandler != nil {
|
||||
wp.panicHandler(r)
|
||||
}
|
||||
if wp.logger != nil {
|
||||
wp.logger.Error(fmt.Sprintf("Task %s panicked: %v", task.ID, r))
|
||||
}
|
||||
}
|
||||
wp.activeTasks.Add(-1)
|
||||
}()
|
||||
|
||||
if err := task.Handler(); err != nil {
|
||||
wp.failedTasks.Add(1)
|
||||
if wp.logger != nil {
|
||||
wp.logger.Error(fmt.Sprintf("Task %s failed: %v", task.ID, err))
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Submit отправляет задачу в пул
|
||||
func (wp *WorkerPool) Submit(task *WorkerTask) error {
|
||||
select {
|
||||
case wp.taskQueue <- task:
|
||||
return nil
|
||||
case <-time.After(5 * time.Second):
|
||||
return fmt.Errorf("task queue full, task rejected: %s", task.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// SubmitFunc отправляет функцию как задачу
|
||||
func (wp *WorkerPool) SubmitFunc(id string, handler func() error) error {
|
||||
return wp.Submit(&WorkerTask{
|
||||
ID: id,
|
||||
Handler: handler,
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
})
|
||||
}
|
||||
|
||||
// GetStats возвращает статистику пула
|
||||
func (wp *WorkerPool) GetStats() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"max_workers": wp.maxWorkers,
|
||||
"active_tasks": wp.activeTasks.Load(),
|
||||
"total_tasks": wp.totalTasks.Load(),
|
||||
"failed_tasks": wp.failedTasks.Load(),
|
||||
"queue_size": len(wp.taskQueue),
|
||||
"queue_capacity": cap(wp.taskQueue),
|
||||
}
|
||||
}
|
||||
|
||||
// GetWorkerPoolStats возвращает статистику в структурированном виде
|
||||
func (wp *WorkerPool) GetWorkerPoolStats() *WorkerPoolStats {
|
||||
return &WorkerPoolStats{
|
||||
MaxWorkers: wp.maxWorkers,
|
||||
ActiveTasks: wp.activeTasks.Load(),
|
||||
TotalTasks: wp.totalTasks.Load(),
|
||||
FailedTasks: wp.failedTasks.Load(),
|
||||
QueueSize: len(wp.taskQueue),
|
||||
QueueCapacity: cap(wp.taskQueue),
|
||||
}
|
||||
}
|
||||
|
||||
// Stop останавливает пул воркеров
|
||||
func (wp *WorkerPool) Stop() {
|
||||
close(wp.stopChan)
|
||||
close(wp.taskQueue)
|
||||
wp.wg.Wait()
|
||||
}
|
||||
|
||||
// SafeGo безопасно запускает горутину с обработкой паники
|
||||
func SafeGo(fn func(), panicHandler func(interface{})) {
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if panicHandler != nil {
|
||||
panicHandler(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
fn()
|
||||
}()
|
||||
}
|
||||
|
||||
// SafeGoWithLogger безопасно запускает горутину с логгером
|
||||
func SafeGoWithLogger(fn func(), logger LoggerInterface, name string) {
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if logger != nil {
|
||||
logger.Error(fmt.Sprintf("Goroutine %s panicked: %v\n%s", name, r, debug.Stack()))
|
||||
}
|
||||
}
|
||||
}()
|
||||
fn()
|
||||
}()
|
||||
}
|
||||
Reference in New Issue
Block a user