Files
oc-scheduler/infrastructure/session/session.go

234 lines
7.8 KiB
Go
Raw Normal View History

2026-03-25 11:11:37 +01:00
package session
import (
"encoding/json"
"fmt"
"oc-scheduler/infrastructure/execution"
"oc-scheduler/infrastructure/scheduling_resources"
"sync"
"time"
"cloud.o-forge.io/core/oc-lib/dbs"
"cloud.o-forge.io/core/oc-lib/models/bill"
"cloud.o-forge.io/core/oc-lib/models/booking"
"cloud.o-forge.io/core/oc-lib/models/common/enum"
"cloud.o-forge.io/core/oc-lib/models/order"
"cloud.o-forge.io/core/oc-lib/models/resources/purchase_resource"
"cloud.o-forge.io/core/oc-lib/models/utils"
"cloud.o-forge.io/core/oc-lib/models/workflow_execution"
"cloud.o-forge.io/core/oc-lib/tools"
oclib "cloud.o-forge.io/core/oc-lib"
)
type SessionExecutionsService struct {
Mu sync.RWMutex
ExecutionsSessionID string
}
func NewSessionExecutionsService(sessionID string) *SessionExecutionsService {
return &SessionExecutionsService{ExecutionsSessionID: sessionID}
}
// ---------------------------------------------------------------------------
// DB helpers
// ---------------------------------------------------------------------------
func (s *SessionExecutionsService) sessionIDFilter(field, id string) *dbs.Filters {
return &dbs.Filters{
And: map[string][]dbs.Filter{
field: {{Operator: dbs.EQUAL.String(), Value: id}},
},
}
}
func (s *SessionExecutionsService) loadSession(dt tools.DataType) []scheduling_resources.SchedulerObject {
results := oclib.NewRequestAdmin(oclib.LibDataEnum(dt), nil).Search(
s.sessionIDFilter("executions_id", s.ExecutionsSessionID), "", true)
out := make([]scheduling_resources.SchedulerObject, 0, len(results.Data))
for _, obj := range results.Data {
out = append(out, scheduling_resources.ToSchedulerObject(dt, obj))
}
return out
}
func (s *SessionExecutionsService) LoadSessionExecs() []*workflow_execution.WorkflowExecution {
adminReq := &tools.APIRequest{Admin: true}
results, _, _ := workflow_execution.NewAccessor(adminReq).Search(
s.sessionIDFilter("executions_id", s.ExecutionsSessionID), "", true)
out := make([]*workflow_execution.WorkflowExecution, 0)
for _, obj := range results {
if exec, ok := obj.(*workflow_execution.WorkflowExecution); ok {
out = append(out, exec)
}
}
return out
}
func (s *SessionExecutionsService) loadSessionOrder() *order.Order {
adminReq := &tools.APIRequest{Admin: true}
results, _, _ := order.NewAccessor(adminReq).Search(
s.sessionIDFilter("executions_id", s.ExecutionsSessionID), "", true)
for _, obj := range results {
if o, ok := obj.(*order.Order); ok {
return o
}
}
return nil
}
// ---------------------------------------------------------------------------
// Session upsert
// ---------------------------------------------------------------------------
func (s *SessionExecutionsService) UpsertSessionDrafts(
purchases, bookings []scheduling_resources.SchedulerObject,
execs []*workflow_execution.WorkflowExecution,
request *tools.APIRequest,
) {
adminReq := &tools.APIRequest{Admin: true}
for dt, datas := range map[tools.DataType][]scheduling_resources.SchedulerObject{
tools.BOOKING: bookings,
tools.PURCHASE_RESOURCE: purchases,
} {
existing := map[string]scheduling_resources.SchedulerObject{}
seen := map[string]bool{}
for _, bk := range s.loadSession(dt) {
existing[bk.GetKey()] = bk
}
s.upsertDrafts(dt, datas, existing, seen, request)
for key, prev := range existing {
if !seen[key] {
scheduling_resources.GetService().Delete(dt, prev, request)
}
}
}
for _, old := range s.LoadSessionExecs() {
execution.UnregisterExecLock(old.GetID())
workflow_execution.NewAccessor(adminReq).DeleteOne(old.GetID())
}
for _, exec := range execs {
exec.ExecutionsID = s.ExecutionsSessionID
exec.IsDraft = true
ex, _, err := utils.GenericStoreOne(exec, workflow_execution.NewAccessor(adminReq))
if err == nil {
execution.RegisterExecLock(ex.GetID())
go execution.WatchDeadline(ex.GetID(), s.ExecutionsSessionID, exec.ExecDate, request)
}
}
if existing := s.loadSessionOrder(); existing == nil {
GenerateOrder(purchases, bookings, s.ExecutionsSessionID, request)
} else {
for _, purch := range purchases {
existing.Purchases = append(existing.Purchases,
scheduling_resources.FromSchedulerObject(tools.PURCHASE_RESOURCE, purch).(*purchase_resource.PurchaseResource))
}
for _, b := range bookings {
existing.Bookings = append(existing.Bookings,
scheduling_resources.FromSchedulerObject(tools.BOOKING, b).(*booking.Booking))
}
utils.GenericRawUpdateOne(existing, existing.GetID(), order.NewAccessor(adminReq))
}
}
func (s *SessionExecutionsService) upsertDrafts(
dt tools.DataType,
datas []scheduling_resources.SchedulerObject,
existing map[string]scheduling_resources.SchedulerObject,
seen map[string]bool,
request *tools.APIRequest,
) {
self := scheduling_resources.GetService().Self()
fmt.Println("upsertDrafts", len(datas), len(existing))
for _, bk := range datas {
if self != nil {
bk.SetSchedulerPeerID(self.PeerID)
}
bk.SetExecutionsID(s.ExecutionsSessionID)
seen[bk.GetKey()] = true
if prev, ok := existing[bk.GetKey()]; ok {
bk.SetID(prev.GetID())
bk.SetIsDraft(false)
needsConsiders := scheduling_resources.GetService().PropagateWrite(
scheduling_resources.FromSchedulerDBObject(dt, bk), bk.GetDestPeer(), dt, request)
if needsConsiders {
if payload, err := json.Marshal(execution.ConsidersPayload{ID: bk.GetID()}); err == nil {
go execution.UpdateExecutionState(payload, dt)
}
}
} else {
errCh := make(chan error, 1)
scheduling_resources.GetService().PropagateCreate(
scheduling_resources.FromSchedulerDBObject(dt, bk), bk.GetDestPeer(), dt, request, errCh)
<-errCh
}
}
}
// ---------------------------------------------------------------------------
// Session lifecycle
// ---------------------------------------------------------------------------
func (s *SessionExecutionsService) CleanupSession(request *tools.APIRequest) {
adminReq := &tools.APIRequest{Admin: true}
for _, exec := range s.LoadSessionExecs() {
execution.Unschedule(exec.GetID(), request)
workflow_execution.NewAccessor(adminReq).DeleteOne(exec.GetID())
}
if o := s.loadSessionOrder(); o != nil {
order.NewAccessor(adminReq).DeleteOne(o.GetID())
}
}
func GenerateOrder(
purchases, bookings []scheduling_resources.SchedulerObject,
executionsID string,
request *tools.APIRequest,
) (string, error) {
newOrder := &order.Order{
AbstractObject: utils.AbstractObject{
Name: "order_" + request.PeerID + "_" + time.Now().UTC().Format("2006-01-02T15:04:05"),
IsDraft: true,
},
ExecutionsID: executionsID,
Purchases: []*purchase_resource.PurchaseResource{},
Bookings: []*booking.Booking{},
Status: enum.PENDING,
}
for _, purch := range purchases {
newOrder.Purchases = append(newOrder.Purchases,
scheduling_resources.FromSchedulerObject(tools.PURCHASE_RESOURCE, purch).(*purchase_resource.PurchaseResource))
}
for _, b := range bookings {
newOrder.Bookings = append(newOrder.Bookings,
scheduling_resources.FromSchedulerObject(tools.BOOKING, b).(*booking.Booking))
}
res, _, err := order.NewAccessor(request).StoreOne(newOrder)
if err != nil {
return "", err
}
if _, err := bill.DraftFirstBill(res.(*order.Order), request); err != nil {
return res.GetID(), err
}
return res.GetID(), nil
}
func (s *SessionExecutionsService) ConfirmSession(request *tools.APIRequest) error {
for _, dt := range []tools.DataType{tools.BOOKING, tools.PURCHASE_RESOURCE} {
for _, bk := range s.loadSession(dt) {
bk.SetIsDraft(false)
needsConsiders := scheduling_resources.GetService().PropagateWrite(
scheduling_resources.FromSchedulerDBObject(dt, bk), bk.GetDestPeer(), dt, request)
if needsConsiders {
if payload, err := json.Marshal(execution.ConsidersPayload{ID: bk.GetID()}); err == nil {
go execution.UpdateExecutionState(payload, dt)
}
}
}
}
return nil
}