113 lines
3.8 KiB
Go
113 lines
3.8 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"google.golang.org/protobuf/proto"
|
|
roomeventsv1 "hyapp.local/api/proto/events/room/v1"
|
|
"hyapp/pkg/appcode"
|
|
"hyapp/pkg/idgen"
|
|
"hyapp/pkg/roommq"
|
|
"hyapp/pkg/xerr"
|
|
"hyapp/services/room-service/internal/integration"
|
|
"hyapp/services/room-service/internal/room/command"
|
|
)
|
|
|
|
// scheduleRoomRocketLaunchFromEnvelope derives the delayed wakeup from the
|
|
// already committed ignited outbox. Scheduling after outbox claim keeps
|
|
// SendGift free of external MQ side effects before MySQL commit.
|
|
func (s *Service) scheduleRoomRocketLaunchFromEnvelope(ctx context.Context, envelope *roomeventsv1.EventEnvelope) error {
|
|
if s.roomRocketLaunchScheduler == nil || envelope == nil || envelope.GetEventType() != "RoomRocketIgnited" {
|
|
return nil
|
|
}
|
|
var event roomeventsv1.RoomRocketIgnited
|
|
if err := proto.Unmarshal(envelope.GetBody(), &event); err != nil {
|
|
return err
|
|
}
|
|
if event.GetRocketId() == "" || event.GetLevel() <= 0 || event.GetLaunchAtMs() <= 0 {
|
|
return fmt.Errorf("room rocket ignited event is incomplete: event_id=%s", envelope.GetEventId())
|
|
}
|
|
return s.roomRocketLaunchScheduler.ScheduleRoomRocketLaunch(ctx, integration.RoomRocketLaunchSchedule{
|
|
AppCode: appcode.Normalize(envelope.GetAppCode()),
|
|
RoomID: envelope.GetRoomId(),
|
|
RocketID: event.GetRocketId(),
|
|
Level: event.GetLevel(),
|
|
LaunchAtMS: event.GetLaunchAtMs(),
|
|
ResetAtMS: event.GetResetAtMs(),
|
|
CommandID: roomRocketLaunchCommandID(event.GetRocketId()),
|
|
})
|
|
}
|
|
|
|
// HandleRoomRocketLaunchDue handles a RocketMQ delayed wakeup. It pre-checks
|
|
// due-ness before writing a command log, because an early-delivered delay
|
|
// message must not poison the deterministic open command_id as a no-op.
|
|
func (s *Service) HandleRoomRocketLaunchDue(ctx context.Context, due roommq.RoomRocketLaunchDueMessage) error {
|
|
ctx = appcode.WithContext(ctx, due.AppCode)
|
|
if due.RoomID == "" || due.RocketID == "" || due.Level <= 0 || due.LaunchAtMS <= 0 {
|
|
return xerr.New(xerr.InvalidArgument, "room rocket launch due message is incomplete")
|
|
}
|
|
|
|
roomCell, lease, err := s.ensureCell(ctx, due.RoomID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
currentState, _, err := roomCell.Snapshot(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
owned, err := s.directory.VerifyOwner(ctx, runtimeRoomKeyFromContext(ctx, due.RoomID), s.nodeID, lease.LeaseToken, s.clock.Now())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !owned {
|
|
return xerr.New(xerr.Conflict, "room rocket launch lease moved")
|
|
}
|
|
|
|
now := s.clock.Now().UTC()
|
|
nowMS := now.UnixMilli()
|
|
if currentState == nil || currentState.Rocket == nil {
|
|
return nil
|
|
}
|
|
rocket := currentState.Rocket
|
|
pending, _, ok := findRocketPendingLaunch(rocket.PendingLaunches, due.RocketID, due.Level)
|
|
if !ok || pending.LaunchAtMS != due.LaunchAtMS {
|
|
// Stale delayed messages are expected after retries or if the room reset/launched by another path.
|
|
return nil
|
|
}
|
|
if pending.ResetAtMS > 0 && nowMS >= pending.ResetAtMS {
|
|
// UTC day reset wins over yesterday's delayed wakeup.
|
|
return nil
|
|
}
|
|
if pending.LaunchAtMS > nowMS {
|
|
return xerr.New(xerr.Unavailable, "room rocket launch wakeup arrived before launch_at_ms")
|
|
}
|
|
|
|
actorUserID := currentState.OwnerUserID
|
|
if actorUserID <= 0 {
|
|
actorUserID = pending.IgniterUserID
|
|
}
|
|
if actorUserID <= 0 {
|
|
return xerr.New(xerr.InvalidArgument, "room rocket launch actor is missing")
|
|
}
|
|
commandID := due.CommandID
|
|
if commandID == "" {
|
|
commandID = roomRocketLaunchCommandID(due.RocketID)
|
|
}
|
|
_, err = s.launchRoomRocket(ctx, command.LaunchRoomRocket{
|
|
Base: command.Base{
|
|
AppCode: appcode.FromContext(ctx),
|
|
RequestID: idgen.New("req_room_rocket_launch_mq"),
|
|
CommandID: commandID,
|
|
ActorID: actorUserID,
|
|
Room: due.RoomID,
|
|
GatewayNodeID: s.nodeID,
|
|
SessionID: "room-rocket-launch-mq",
|
|
SentAtMS: nowMS,
|
|
},
|
|
RocketID: due.RocketID,
|
|
Level: due.Level,
|
|
})
|
|
return err
|
|
}
|