fix(landscape): guard redelivery, detach terminal writes, bound workers
F2: Execute had no status guard, so a redelivered or stale message reran a finished run. Runs that are neither queued nor running are logged and acked without work; "running" still executes so a worker that crashed mid-run recovers. F4: failRun, finishNodeFailed and MarkRunFailed wrote the terminal status on the caller's context, which is exactly the context that is already cancelled when the run failed because of a shutdown or a sibling node — the write was refused and the run stayed stuck in "running". They now write on a context.WithoutCancel copy with a 10s timeout; the consumer's best-effort MarkRunFailed does the same. F5: peak memory scaled with the host's core count. LP_MAX_WORKERS (default 8) is now the total window-worker budget of a run and LP_MAX_NODES (default 2) the node fan-out; each node gets max(1, workers/nodes) through the new landscape.ComputeWithWorkers. Compute and service.ComputeFunc keep their signatures: Compute delegates with MaxWorkersPerNode, and a legacy ComputeFunc handed to NewLandscapeService is wrapped. F6: the stage loop used a plain errgroup, so a failing node let its siblings run to completion. It now uses errgroup.WithContext and passes the group context into runNode, cancelling siblings mid-compute. F7: wrap every float32 product in an explicit conversion so the compiler cannot fuse it into an FMA, keeping the kernels bit-identical to numpy's per-op rounding. F8: README's builder package list now matches the Dockerfile (adds g++), and the Landscape section documents the memory model and the two new knobs. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,6 @@ import (
|
||||
"gis/api"
|
||||
"gis/internal/config"
|
||||
"gis/internal/domain"
|
||||
"gis/internal/landscape"
|
||||
"gis/internal/messaging/rabbitmq"
|
||||
"gis/internal/parser"
|
||||
"gis/internal/platform/logger"
|
||||
@@ -92,9 +91,15 @@ func New(ctx context.Context) (*App, error) {
|
||||
categories: service.NewCategoryService(categoryRepo),
|
||||
datasets: datasetsSvc,
|
||||
groups: service.NewDatasetGroupService(groupRepo, datasetRepo, store),
|
||||
// A nil ComputeFunc selects landscape.ComputeWithWorkers, so each node
|
||||
// gets the per-node share of the configured worker budget.
|
||||
landscape: service.NewLandscapeService(
|
||||
lpRepo, datasetRepo, groupRepo, categoryRepo, datasetsSvc, store,
|
||||
lpJobs, raster.NewGodalIO(), landscape.Compute, log,
|
||||
lpJobs, raster.NewGodalIO(), nil, log,
|
||||
service.LandscapeOptions{
|
||||
MaxWorkers: cfg.Landscape.MaxWorkers,
|
||||
MaxConcurrentNodes: cfg.Landscape.MaxConcurrentNodes,
|
||||
},
|
||||
),
|
||||
eventRepo: eventRepo,
|
||||
}, nil
|
||||
|
||||
Reference in New Issue
Block a user