25 Commits

Author SHA1 Message Date
mr
23a9d648d2 Merge branch 'main' of https://cloud.o-forge.io/core/oc-lib into main
merge oclib
2025-06-12 10:42:38 +02:00
mr
a3029fa3f9 refactor addition in oclib for better use 2025-06-12 10:42:05 +02:00
pb
387785b40c reimported logs without import cycle 2025-06-04 10:33:00 +02:00
pb
03dea55131 added another error log when the status is dead 2025-06-03 10:00:47 +02:00
pb
7b8aa989f6 added a log when an API is not reachable 2025-06-02 18:22:08 +02:00
pb
6ab6383144 corrected an use of the original http caller instead of the deep copy 2025-05-27 18:07:00 +02:00
pb
690d60f9d6 corrected the getBooking function parameters 2025-05-27 16:13:46 +02:00
pb
da0de80afd Booking check and booking post have been transformed in goroutine to improve performance when booking several execution with cron expressions 2025-05-27 15:38:24 +02:00
pb
cd7ae788b1 didn't put the blocking loop in the right place for post booking 2025-05-27 12:06:10 +02:00
pb
0d96cc53bf transformed the loop that posted the booking on oc-datacenter to a threaded operation where each call is done in a goroutine 2025-05-27 11:58:55 +02:00
pb
66fc3c5b35 added the passing of the request.Caller's URL to the deep copy 2025-05-27 11:34:44 +02:00
pb
5ab3eb8a38 forgot to pass the mutex as pointer and unlock it 2025-05-27 11:17:10 +02:00
pb
fec23b4acd modified HTTP caller to have a DeepCopy() method in order to parallelize calls without race conditions 2025-05-27 11:08:35 +02:00
pb
901622fee0 logging on the booking uuid before the post booking 2025-05-27 09:51:41 +02:00
pb
527e622774 correct the error channel 2025-05-26 19:21:28 +02:00
pb
7223b79fe8 correct the error channel 2025-05-26 19:16:39 +02:00
pb
1ade41aeae moved the code that execute the booking into a separated function so that it can be launched as goroutine and parallelize get booking$ 2025-05-26 19:05:17 +02:00
pb
58dc579255 added debug logging 2025-05-26 18:30:56 +02:00
pb
370dac201b In CheckBooking mooved the loop on bookings outside of the loop of execs, which seems to repeat the Peer execution on booking an exponential number of time 2025-05-26 17:55:45 +02:00
pb
2a763006db counting round in exec 2025-05-26 17:41:26 +02:00
pb
522c66653b Added logging for debug 2025-05-26 17:22:09 +02:00
pb
b57f050b81 increased the limit of returns by Mongo find() 2025-05-22 14:41:38 +02:00
pb
41ebcf150a added logging when booking 2025-05-07 18:16:38 +02:00
pb
1499def6ad added booking on the computing and data resource's peers 2025-05-07 14:54:31 +02:00
pb
adbab0f5d7 added more info on error returned by LaunchPeerExecution() 2025-04-30 16:13:49 +02:00
7 changed files with 141 additions and 42 deletions

View File

@@ -287,7 +287,7 @@ func (m *MongoDB) Search(filters *dbs.Filters, collection_name string) (*mongo.C
return nil, 503, err return nil, 503, err
} }
opts := options.Find() opts := options.Find()
opts.SetLimit(100) opts.SetLimit(1000)
targetDBCollection := CollectionMap[collection_name] targetDBCollection := CollectionMap[collection_name]
orList := bson.A{} orList := bson.A{}
andList := bson.A{} andList := bson.A{}

View File

@@ -29,28 +29,7 @@ type PeerCache struct {
// urlFormat formats the URL of the peer with the data type API function // urlFormat formats the URL of the peer with the data type API function
func (p *PeerCache) urlFormat(hostUrl string, dt tools.DataType) string { func (p *PeerCache) urlFormat(hostUrl string, dt tools.DataType) string {
// localhost is replaced by the local peer URL return hostUrl + "/" + strings.ReplaceAll(dt.API(), "oc-", "")
// because localhost must collide on a web request security protocol
/*localhost := ""
if strings.Contains(hostUrl, "localhost") {
localhost = "localhost"
}
if strings.Contains(hostUrl, "127.0.0.1") {
localhost = "127.0.0.1"
}
if localhost != "" {
r := regexp.MustCompile("(" + localhost + ":[0-9]+)")
t := r.FindString(hostUrl)
if t != "" {
hostUrl = strings.Replace(hostUrl, t, dt.API()+":8080/oc", -1)
} else {
hostUrl = strings.ReplaceAll(hostUrl, localhost, dt.API()+":8080/oc")
}
} else {*/
hostUrl = hostUrl + "/" + strings.ReplaceAll(dt.API(), "oc-", "")
//}
fmt.Println("Contacting", hostUrl)
return hostUrl
} }
// checkPeerStatus checks the status of a peer // checkPeerStatus checks the status of a peer
@@ -86,20 +65,20 @@ func (p *PeerCache) LaunchPeerExecution(peerID string, dataID string,
// If the peer is not reachable, add the execution to the failed executions list // If the peer is not reachable, add the execution to the failed executions list
pexec := &PeerExecution{ pexec := &PeerExecution{
Method: method.String(), Method: method.String(),
Url: p.urlFormat((mypeer.Url), dt) + path, // the url is constitued of : host URL + resource path + action path (ex : mypeer.com/datacenter/resourcetype/path/to/action) Url: p.urlFormat((mypeer.Url), dt) + path, // the url is constitued of : host URL + resource path + action path (ex : mypeer.com/datacenter/resourcetype/path/to/action)
Body: body, Body: body,
DataType: dt.EnumIndex(), DataType: dt.EnumIndex(),
DataID: dataID, DataID: dataID,
} }
mypeer.AddExecution(*pexec) mypeer.AddExecution(*pexec)
NewShallowAccessor().UpdateOne(mypeer, peerID) // Update the peer in the db NewShallowAccessor().UpdateOne(mypeer, peerID) // Update the peer in the db
return nil, errors.New("peer is not reachable") return nil, errors.New("peer is " + peerID + " not reachable")
} else { } else {
if mypeer == nil { if mypeer == nil {
return nil, errors.New("peer not found") return nil, errors.New("peer " + peerID + " not found")
} }
// If the peer is reachable, launch the execution // If the peer is reachable, launch the execution
url = p.urlFormat((mypeer.Url), dt) + path // Format the URL url = p.urlFormat((mypeer.Url), dt) + path // Format the URL
tmp := mypeer.FailedExecution // Get the failed executions list tmp := mypeer.FailedExecution // Get the failed executions list
mypeer.FailedExecution = []PeerExecution{} // Reset the failed executions list mypeer.FailedExecution = []PeerExecution{} // Reset the failed executions list
NewShallowAccessor().UpdateOne(mypeer, peerID) // Update the peer in the db NewShallowAccessor().UpdateOne(mypeer, peerID) // Update the peer in the db

View File

@@ -31,6 +31,43 @@ func (d *Workflow) GetAccessor(request *tools.APIRequest) utils.Accessor {
return NewAccessor(request) // Create a new instance of the accessor return NewAccessor(request) // Create a new instance of the accessor
} }
type Deps struct {
Source string
Dest string
}
func (w *Workflow) isDependancy(id string) []Deps {
dependancyOfIDs := []Deps{}
for _, link := range w.Graph.Links {
if _, ok := w.Graph.Items[link.Destination.ID]; !ok {
continue
}
source := w.Graph.Items[link.Destination.ID].Processing
if id == link.Source.ID && source != nil {
dependancyOfIDs = append(dependancyOfIDs, Deps{Source: source.GetName(), Dest: link.Destination.ID})
}
sourceWF := w.Graph.Items[link.Destination.ID].Workflow
if id == link.Source.ID && sourceWF != nil {
dependancyOfIDs = append(dependancyOfIDs, Deps{Source: sourceWF.GetName(), Dest: link.Destination.ID})
}
}
return dependancyOfIDs
}
func (w *Workflow) GetDependencies(id string) (dependencies []Deps) {
for _, link := range w.Graph.Links {
if _, ok := w.Graph.Items[link.Source.ID]; !ok {
continue
}
source := w.Graph.Items[link.Source.ID].Processing
if id == link.Destination.ID && source != nil {
dependencies = append(dependencies, Deps{Source: source.GetName(), Dest: link.Source.ID})
continue
}
}
return
}
func (w *Workflow) GetGraphItems(f func(item graph.GraphItem) bool) (list_datas []graph.GraphItem) { func (w *Workflow) GetGraphItems(f func(item graph.GraphItem) bool) (list_datas []graph.GraphItem) {
for _, item := range w.Graph.Items { for _, item := range w.Graph.Items {
if f(item) { if f(item) {

View File

@@ -112,6 +112,8 @@ func (d *WorkflowExecution) VerifyAuth(request *tools.APIRequest) bool {
func (d *WorkflowExecution) Book(executionsID string, wfID string, priceds map[tools.DataType]map[string]pricing.PricedItemITF) []*booking.Booking { func (d *WorkflowExecution) Book(executionsID string, wfID string, priceds map[tools.DataType]map[string]pricing.PricedItemITF) []*booking.Booking {
booking := d.bookEach(executionsID, wfID, tools.STORAGE_RESOURCE, priceds[tools.STORAGE_RESOURCE]) booking := d.bookEach(executionsID, wfID, tools.STORAGE_RESOURCE, priceds[tools.STORAGE_RESOURCE])
booking = append(booking, d.bookEach(executionsID, wfID, tools.PROCESSING_RESOURCE, priceds[tools.PROCESSING_RESOURCE])...) booking = append(booking, d.bookEach(executionsID, wfID, tools.PROCESSING_RESOURCE, priceds[tools.PROCESSING_RESOURCE])...)
booking = append(booking,d.bookEach(executionsID, wfID, tools.COMPUTE_RESOURCE, priceds[tools.COMPUTE_RESOURCE])...)
booking = append(booking,d.bookEach(executionsID, wfID, tools.DATA_RESOURCE, priceds[tools.DATA_RESOURCE])...)
return booking return booking
} }

View File

@@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
"sync"
"time" "time"
"cloud.o-forge.io/core/oc-lib/models/booking" "cloud.o-forge.io/core/oc-lib/models/booking"
@@ -77,22 +78,60 @@ func (ws *WorkflowSchedule) CheckBooking(wfID string, request *tools.APIRequest)
bookings := []*booking.Booking{} bookings := []*booking.Booking{}
for _, exec := range execs { for _, exec := range execs {
bookings = append(bookings, exec.Book(ws.UUID, wfID, priceds)...) bookings = append(bookings, exec.Book(ws.UUID, wfID, priceds)...)
for _, b := range bookings {
meth := request.Caller.URLS[tools.BOOKING][tools.GET]
meth = strings.ReplaceAll(meth, ":id", b.ResourceID)
meth = strings.ReplaceAll(meth, ":start_date", b.ExpectedStartDate.Format("2006-01-02T15:04:05"))
meth = strings.ReplaceAll(meth, ":end_date", b.ExpectedEndDate.Format("2006-01-02T15:04:05"))
request.Caller.URLS[tools.BOOKING][tools.GET] = meth
_, err := (&peer.Peer{}).LaunchPeerExecution(b.DestPeerID, b.ResourceID, tools.BOOKING, tools.GET, nil, request.Caller)
if err != nil {
return false, wf, execs, bookings, err
}
}
} }
errCh := make(chan error, len(bookings))
var m sync.Mutex
for _, b := range bookings {
go getBooking(b, request, wf, execs, bookings, errCh, &m)
}
for i := 0; i < len(bookings); i++ {
if err := <-errCh; err != nil {
return false, wf, execs, bookings, err
}
}
return true, wf, execs, bookings, nil return true, wf, execs, bookings, nil
} }
func getBooking( b *booking.Booking, request *tools.APIRequest, wf *workflow.Workflow, execs []*WorkflowExecution, bookings []*booking.Booking, errCh chan error, m *sync.Mutex) {
m.Lock()
c, err := getCallerCopy(request, errCh)
if err != nil {
errCh <- err
return
}
m.Unlock()
meth := c.URLS[tools.BOOKING][tools.GET]
meth = strings.ReplaceAll(meth, ":id", b.ResourceID)
meth = strings.ReplaceAll(meth, ":start_date", b.ExpectedStartDate.Format("2006-01-02T15:04:05"))
meth = strings.ReplaceAll(meth, ":end_date", b.ExpectedEndDate.Format("2006-01-02T15:04:05"))
c.URLS[tools.BOOKING][tools.GET] = meth
_, err = (&peer.Peer{}).LaunchPeerExecution(b.DestPeerID, b.ResourceID, tools.BOOKING, tools.GET, nil, &c)
if err != nil {
errCh <- err
return
}
errCh <- nil
}
func getCallerCopy(request *tools.APIRequest, errCh chan error) (tools.HTTPCaller, error) {
var c tools.HTTPCaller
err := request.Caller.DeepCopy(c)
if err != nil {
errCh <- err
return tools.HTTPCaller{}, nil
}
c.URLS = request.Caller.URLS
return c, err
}
func (ws *WorkflowSchedule) Schedules(wfID string, request *tools.APIRequest) (*WorkflowSchedule, *workflow.Workflow, []*WorkflowExecution, error) { func (ws *WorkflowSchedule) Schedules(wfID string, request *tools.APIRequest) (*WorkflowSchedule, *workflow.Workflow, []*WorkflowExecution, error) {
if request == nil { if request == nil {
return ws, nil, []*WorkflowExecution{}, errors.New("no request found") return ws, nil, []*WorkflowExecution{}, errors.New("no request found")
@@ -111,13 +150,20 @@ func (ws *WorkflowSchedule) Schedules(wfID string, request *tools.APIRequest) (*
return ws, nil, executions, errors.New("could not book the workflow : " + fmt.Sprintf("%v", err)) return ws, nil, executions, errors.New("could not book the workflow : " + fmt.Sprintf("%v", err))
} }
ws.Workflow = wf ws.Workflow = wf
var errCh = make(chan error, len(bookings))
var m sync.Mutex
for _, booking := range bookings { for _, booking := range bookings {
_, err := (&peer.Peer{}).LaunchPeerExecution(booking.DestPeerID, "", go ws.BookExecs(booking, request, errCh, &m)
tools.BOOKING, tools.POST, booking.Serialize(booking), request.Caller) }
if err != nil {
for i := 0; i < len(bookings); i++ {
if err := <- errCh ; err != nil {
return ws, wf, executions, errors.New("could not launch the peer execution : " + fmt.Sprintf("%v", err)) return ws, wf, executions, errors.New("could not launch the peer execution : " + fmt.Sprintf("%v", err))
} }
} }
fmt.Println("Schedules") fmt.Println("Schedules")
for _, exec := range executions { for _, exec := range executions {
err := exec.PurgeDraft(request) err := exec.PurgeDraft(request)
@@ -131,6 +177,27 @@ func (ws *WorkflowSchedule) Schedules(wfID string, request *tools.APIRequest) (*
return ws, wf, executions, nil return ws, wf, executions, nil
} }
func (ws *WorkflowSchedule) BookExecs(booking *booking.Booking, request *tools.APIRequest, errCh chan error, m *sync.Mutex) {
m.Lock()
c, err := getCallerCopy(request, errCh)
if err != nil {
errCh <- err
return
}
m.Unlock()
_, err = (&peer.Peer{}).LaunchPeerExecution(booking.DestPeerID, "",
tools.BOOKING, tools.POST, booking.Serialize(booking), &c)
if err != nil {
errCh <- err
return
}
errCh <- nil
}
/* /*
BOOKING IMPLIED TIME, not of subscription but of execution BOOKING IMPLIED TIME, not of subscription but of execution
so is processing time execution time applied on computes so is processing time execution time applied on computes

View File

@@ -7,6 +7,7 @@ import (
"cloud.o-forge.io/core/oc-lib/config" "cloud.o-forge.io/core/oc-lib/config"
"cloud.o-forge.io/core/oc-lib/dbs/mongo" "cloud.o-forge.io/core/oc-lib/dbs/mongo"
"cloud.o-forge.io/core/oc-lib/logs"
beego "github.com/beego/beego/v2/server/web" beego "github.com/beego/beego/v2/server/web"
) )
@@ -135,6 +136,7 @@ func (a *API) CheckRemotePeer(url string) (State, map[string]int) {
// CheckRemoteAPIs checks the state of remote APIs from your proper OC // CheckRemoteAPIs checks the state of remote APIs from your proper OC
func (a *API) CheckRemoteAPIs(apis []DataType) (State, map[string]string, error) { func (a *API) CheckRemoteAPIs(apis []DataType) (State, map[string]string, error) {
// Check if the database is up // Check if the database is up
l := logs.GetLogger()
new := map[string]string{} new := map[string]string{}
caller := NewHTTPCaller(map[DataType]map[METHOD]string{}) // Create a new http caller caller := NewHTTPCaller(map[DataType]map[METHOD]string{}) // Create a new http caller
code := 0 code := 0
@@ -145,6 +147,7 @@ func (a *API) CheckRemoteAPIs(apis []DataType) (State, map[string]string, error)
var resp APIStatusResponse var resp APIStatusResponse
b, err := caller.CallGet("http://"+api.API()+":8080", "/oc/version/status") // Call the status endpoint of the remote API (standard OC status endpoint) b, err := caller.CallGet("http://"+api.API()+":8080", "/oc/version/status") // Call the status endpoint of the remote API (standard OC status endpoint)
if err != nil { if err != nil {
l.Error().Msg(api.String() + " not reachable")
state = REDUCED_SERVICE // If a remote API is not reachable, return reduced service state = REDUCED_SERVICE // If a remote API is not reachable, return reduced service
continue continue
} }
@@ -161,6 +164,7 @@ func (a *API) CheckRemoteAPIs(apis []DataType) (State, map[string]string, error)
reachable = true // If the remote API is reachable, set reachable to true cause we are not dead reachable = true // If the remote API is reachable, set reachable to true cause we are not dead
} }
if !reachable { if !reachable {
l.Error().Msg("Peer check returned no answers")
state = DEAD // If no remote API is reachable, return dead, nobody is alive state = DEAD // If no remote API is reachable, return dead, nobody is alive
} }
if code > 0 { if code > 0 {

View File

@@ -63,6 +63,16 @@ func NewHTTPCaller(urls map[DataType]map[METHOD]string) *HTTPCaller {
} }
} }
// Creates a copy of the current caller, in order to have parallelized executions without race condition
func (c* HTTPCaller) DeepCopy(dst HTTPCaller) error {
bytes, err := json.Marshal(c)
if err != nil {
return err
}
return json.Unmarshal(bytes, &dst)
}
// CallGet calls the GET method on the HTTP server // CallGet calls the GET method on the HTTP server
func (caller *HTTPCaller) CallGet(url string, subpath string, types ...string) ([]byte, error) { func (caller *HTTPCaller) CallGet(url string, subpath string, types ...string) ([]byte, error) {
req, err := http.NewRequest(http.MethodGet, url+subpath, bytes.NewBuffer([]byte(""))) req, err := http.NewRequest(http.MethodGet, url+subpath, bytes.NewBuffer([]byte("")))