mirror of
https://github.com/hanzoai/tasks.git
synced 2026-11-02 12:22:18 +00:00
## What changed? Added Chasm RPC handler foundation and interceptor. Added test as use case demo. ## Why? The Chasm RPC handler will be used as the abstraction layer for future work. A grpc interceptor was added to intercept requests to process boilerplate code so the handlers don't have to regurgitate it. Please look at the added test as it demonstrates how the RPC handler is registered and a request gets processed. ## How did you test it? - [X] built - [X] run locally and tested manually - [X] covered by existing tests - [X] added new unit test(s) - [ ] added new functional test(s) ## Potential risks There is a [future ticket](https://temporalio.atlassian.net/browse/ACT-69) to refactor all history RPC calls to start using the interceptor as to avoid boilerplate code. This does need a deeper discussion with the team and we also need to figure out how to identify the incoming RPC request so we can extract namespace etc. properly. --------- Co-authored-by: Roey Berman <[email protected]>
166 lines
5.3 KiB
Go
166 lines
5.3 KiB
Go
package history
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"time"
|
|
|
|
"go.temporal.io/server/api/historyservice/v1"
|
|
"go.temporal.io/server/chasm"
|
|
"go.temporal.io/server/common/log"
|
|
"go.temporal.io/server/common/log/tag"
|
|
"go.temporal.io/server/common/membership"
|
|
"go.temporal.io/server/common/metrics"
|
|
"go.temporal.io/server/common/persistence/visibility/manager"
|
|
"go.temporal.io/server/common/util"
|
|
"go.temporal.io/server/service/history/configs"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/health"
|
|
healthpb "google.golang.org/grpc/health/grpc_health_v1"
|
|
"google.golang.org/grpc/reflection"
|
|
)
|
|
|
|
// Service represents the history service
|
|
type (
|
|
Service struct {
|
|
handler *Handler
|
|
visibilityManager manager.VisibilityManager
|
|
config *configs.Config
|
|
|
|
server *grpc.Server
|
|
logger log.Logger
|
|
grpcListener net.Listener
|
|
membershipMonitor membership.Monitor
|
|
metricsHandler metrics.Handler
|
|
healthServer *health.Server
|
|
readinessCancel context.CancelFunc
|
|
chasmRegistry *chasm.Registry
|
|
}
|
|
)
|
|
|
|
func NewService(
|
|
server *grpc.Server,
|
|
serviceConfig *configs.Config,
|
|
visibilityMgr manager.VisibilityManager,
|
|
handler *Handler,
|
|
logger log.Logger,
|
|
grpcListener net.Listener,
|
|
membershipMonitor membership.Monitor,
|
|
metricsHandler metrics.Handler,
|
|
healthServer *health.Server,
|
|
chasmRegistry *chasm.Registry,
|
|
) *Service {
|
|
return &Service{
|
|
server: server,
|
|
handler: handler,
|
|
visibilityManager: visibilityMgr,
|
|
config: serviceConfig,
|
|
logger: logger,
|
|
grpcListener: grpcListener,
|
|
membershipMonitor: membershipMonitor,
|
|
metricsHandler: metricsHandler,
|
|
healthServer: healthServer,
|
|
chasmRegistry: chasmRegistry,
|
|
}
|
|
}
|
|
|
|
// Start starts the service
|
|
func (s *Service) Start() {
|
|
s.logger.Info("history starting")
|
|
|
|
metrics.RestartCount.With(s.metricsHandler).Record(1)
|
|
|
|
s.handler.Start()
|
|
|
|
historyservice.RegisterHistoryServiceServer(s.server, s.handler)
|
|
healthpb.RegisterHealthServer(s.server, s.healthServer)
|
|
s.chasmRegistry.RegisterServices(s.server)
|
|
|
|
// start as NOT_SERVING, update to SERVING after initial shards acquired
|
|
s.healthServer.SetServingStatus(serviceName, healthpb.HealthCheckResponse_NOT_SERVING)
|
|
readinessCtx, readinessCancel := context.WithCancel(context.Background())
|
|
s.readinessCancel = readinessCancel
|
|
go func() {
|
|
if s.handler.controller.InitialShardsAcquired(readinessCtx) == nil {
|
|
// add a few seconds for stabilization
|
|
if util.InterruptibleSleep(readinessCtx, 5*time.Second) == nil {
|
|
s.healthServer.SetServingStatus(serviceName, healthpb.HealthCheckResponse_SERVING)
|
|
}
|
|
}
|
|
}()
|
|
|
|
reflection.Register(s.server)
|
|
|
|
go func() {
|
|
s.logger.Info("Starting to serve on history listener")
|
|
if err := s.server.Serve(s.grpcListener); err != nil {
|
|
s.logger.Fatal("Failed to serve on history listener", tag.Error(err))
|
|
}
|
|
}()
|
|
|
|
// As soon as we join membership, other hosts will send requests for shards that we own,
|
|
// so we should try to start this after starting the gRPC server.
|
|
go func() {
|
|
if delay := s.config.StartupMembershipJoinDelay(); delay > 0 {
|
|
// In some situations, like rolling upgrades of the history service,
|
|
// pausing before joining membership can help separate the shard movement
|
|
// caused by another history instance terminating with this instance starting.
|
|
s.logger.Info("history start: delaying before membership start",
|
|
tag.NewDurationTag("startupMembershipJoinDelay", delay))
|
|
time.Sleep(delay)
|
|
}
|
|
s.membershipMonitor.Start()
|
|
}()
|
|
}
|
|
|
|
// Stop stops the service
|
|
func (s *Service) Stop() {
|
|
s.readinessCancel()
|
|
|
|
// remove self from membership ring and wait for traffic to drain
|
|
var err error
|
|
var waitTime time.Duration
|
|
if align := s.config.AlignMembershipChange(); align > 0 {
|
|
propagation := s.membershipMonitor.ApproximateMaxPropagationTime()
|
|
asOf := util.NextAlignedTime(time.Now().Add(propagation), align)
|
|
s.logger.Info("ShutdownHandler: Evicting self from membership ring as of", tag.Timestamp(asOf))
|
|
waitTime, err = s.membershipMonitor.EvictSelfAt(asOf)
|
|
} else {
|
|
s.logger.Info("ShutdownHandler: Evicting self from membership ring immediately")
|
|
err = s.membershipMonitor.EvictSelf()
|
|
}
|
|
if err != nil {
|
|
s.logger.Error("ShutdownHandler: Failed to evict self from membership ring", tag.Error(err))
|
|
}
|
|
s.healthServer.SetServingStatus(serviceName, healthpb.HealthCheckResponse_NOT_SERVING)
|
|
|
|
s.logger.Info("ShutdownHandler: Waiting for drain")
|
|
if waitTime > 0 {
|
|
time.Sleep(
|
|
waitTime + // wait for membership change
|
|
s.config.ShardLingerTimeLimit() + // after membership change shards may linger before close
|
|
s.config.ShardFinalizerTimeout(), // and then take this long to run a finalizer
|
|
)
|
|
} else {
|
|
time.Sleep(s.config.ShutdownDrainDuration())
|
|
}
|
|
|
|
// Stop shard controller. We should have waited long enough for all shards to realize they
|
|
// lost ownership and close, but if not, this will definitely close them.
|
|
s.logger.Info("ShutdownHandler: Initiating shardController shutdown")
|
|
s.handler.controller.Stop()
|
|
|
|
// All grpc handlers should be cancelled now. Give them a little time to return.
|
|
t := time.AfterFunc(2*time.Second, func() {
|
|
s.logger.Info("ShutdownHandler: Drain time expired, stopping all traffic")
|
|
s.server.Stop()
|
|
})
|
|
s.server.GracefulStop()
|
|
t.Stop()
|
|
|
|
s.handler.Stop()
|
|
s.visibilityManager.Close()
|
|
|
|
s.logger.Info("history stopped")
|
|
}
|