139 lines
3.9 KiB
Go
139 lines
3.9 KiB
Go
// Package app wires cron-service configuration, scheduler, storage and gRPC health.
|
|
package app
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials/insecure"
|
|
healthgrpc "google.golang.org/grpc/health/grpc_health_v1"
|
|
activityv1 "hyapp.local/api/proto/activity/v1"
|
|
userv1 "hyapp.local/api/proto/user/v1"
|
|
"hyapp/pkg/grpchealth"
|
|
"hyapp/pkg/logx"
|
|
"hyapp/services/cron-service/internal/config"
|
|
"hyapp/services/cron-service/internal/integration"
|
|
"hyapp/services/cron-service/internal/scheduler"
|
|
mysqlstorage "hyapp/services/cron-service/internal/storage/mysql"
|
|
)
|
|
|
|
// App owns the cron-service process lifecycle.
|
|
type App struct {
|
|
server *grpc.Server
|
|
listener net.Listener
|
|
health *grpchealth.ServingChecker
|
|
repository *mysqlstorage.Repository
|
|
scheduler *scheduler.Scheduler
|
|
userConn *grpc.ClientConn
|
|
activityConn *grpc.ClientConn
|
|
workerCtx context.Context
|
|
workerCancel context.CancelFunc
|
|
workerWG sync.WaitGroup
|
|
closeOnce sync.Once
|
|
}
|
|
|
|
// New initializes cron metadata storage, health service and scheduler.
|
|
func New(cfg config.Config) (*App, error) {
|
|
startupCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
// cron-service owns only scheduler metadata. Business data stays in owner service databases.
|
|
repository, err := mysqlstorage.Open(startupCtx, cfg.MySQLDSN)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
listener, err := net.Listen("tcp", cfg.GRPCAddr)
|
|
if err != nil {
|
|
_ = repository.Close()
|
|
return nil, err
|
|
}
|
|
userConn, err := grpc.Dial(cfg.UserServiceAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
if err != nil {
|
|
_ = listener.Close()
|
|
_ = repository.Close()
|
|
return nil, err
|
|
}
|
|
activityConn, err := grpc.Dial(cfg.ActivityServiceAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
if err != nil {
|
|
_ = userConn.Close()
|
|
_ = listener.Close()
|
|
_ = repository.Close()
|
|
return nil, err
|
|
}
|
|
|
|
server := grpc.NewServer(grpc.UnaryInterceptor(logx.UnaryServerInterceptor("cron-service")))
|
|
health := grpchealth.NewServingChecker("cron-service")
|
|
healthgrpc.RegisterHealthServer(server, grpchealth.NewServer(health))
|
|
|
|
workerCtx, workerCancel := context.WithCancel(context.Background())
|
|
userCron := integration.NewUserCronClient(userv1.NewUserCronServiceClient(userConn))
|
|
activityCron := integration.NewActivityCronClient(activityv1.NewActivityCronServiceClient(activityConn))
|
|
taskScheduler := scheduler.New(cfg.NodeID, cfg.Tasks, repository, map[string]scheduler.Handler{
|
|
"login_ip_risk": userCron.ProcessLoginIPRiskBatch,
|
|
"user_region_rebuild": userCron.ProcessRegionRebuildBatch,
|
|
"mic_open_session_compensation": userCron.CompensateMicOpenSessions,
|
|
"message_fanout": activityCron.ProcessMessageFanoutBatch,
|
|
})
|
|
|
|
return &App{
|
|
server: server,
|
|
listener: listener,
|
|
health: health,
|
|
repository: repository,
|
|
scheduler: taskScheduler,
|
|
userConn: userConn,
|
|
activityConn: activityConn,
|
|
workerCtx: workerCtx,
|
|
workerCancel: workerCancel,
|
|
}, nil
|
|
}
|
|
|
|
// Run starts scheduler loops and gRPC health serving.
|
|
func (a *App) Run() error {
|
|
a.health.MarkServing()
|
|
defer func() {
|
|
if a.workerCancel != nil {
|
|
a.workerCancel()
|
|
}
|
|
a.workerWG.Wait()
|
|
}()
|
|
if a.scheduler != nil {
|
|
a.workerWG.Go(func() {
|
|
a.scheduler.Run(a.workerCtx)
|
|
})
|
|
}
|
|
|
|
err := a.server.Serve(a.listener)
|
|
a.health.MarkStopped()
|
|
if errors.Is(err, grpc.ErrServerStopped) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Close drains scheduler loops before closing gRPC and MySQL resources.
|
|
func (a *App) Close() {
|
|
a.closeOnce.Do(func() {
|
|
a.health.MarkDraining()
|
|
if a.workerCancel != nil {
|
|
a.workerCancel()
|
|
}
|
|
a.workerWG.Wait()
|
|
a.server.GracefulStop()
|
|
if a.repository != nil {
|
|
_ = a.repository.Close()
|
|
}
|
|
if a.userConn != nil {
|
|
_ = a.userConn.Close()
|
|
}
|
|
if a.activityConn != nil {
|
|
_ = a.activityConn.Close()
|
|
}
|
|
})
|
|
}
|