Commit Graph
100 Commits
Author SHA1 Message Date
Axel Uhl 2f5a5c1bc1 bug48: apply the trivial-changes path filter to approved-PR builds too
The changes job used dorny/paths-filter without an explicit base/ref, so on
a pull_request_review event (which, unlike pull_request, is not auto-detected
by the action) it diffed against the default branch with a shallow clone and
treated every file as changed. A docs-only PR therefore ran a full build and
hit the deployment-review gate needlessly.

Diff the PR head (github.event.pull_request.head.sha) against the PR base
(github.event.pull_request.base.sha) explicitly, fetch full history so the
merge-base resolves, and grant pull-requests: read. Push and workflow_dispatch
behavior is unchanged (base/ref fall back to empty, fetch-depth to 1).

Assisted-By: Claude Opus 4.8
2026-08-27 11:21:54 +02:00
Axel Uhl cfa5cf505c more instructions for Claude Code in CLAUDE.md 2026-08-27 10:41:52 +02:00
Axel Uhl 0ed071ef8d bug48: run release build for PRs only after an approving review
Add a pull_request_review (submitted) trigger and gate the changes/build
jobs on an approving review, so a PR (possibly from an untrusted fork)
never builds with secrets before a maintainer has vetted the exact code.

Secrets are released via a conditional 'privileged-pr-build' environment
referenced only on review runs; on push/workflow_dispatch the environment
name is empty (no gate), keeping existing behavior unchanged. The
environment holds no secrets, so repository-level values are used via
environment>repository precedence and rotation stays in one place.

Review runs check out the PR head SHA with persist-credentials: false so
untrusted build code cannot scrape the token. build-gate now treats a
skipped changes job (non-approving review) as pass rather than failure.

Assisted-By: Claude Opus 4.8
2026-08-27 00:08:00 +02:00
Axel Uhl 2662d15f4b new i18n messages for Sail Insight 2026-08-25 18:07:59 +02:00
Axel Uhl 86e6d8caed added YellowBrick test for RMSR2025 and factored commonalities with RMSR2019 2026-08-24 14:06:14 +02:00
Axel Uhl 2ea18ceddd Merge branch 'eclipse-main' into translation 2026-08-21 13:06:32 +02:00
Axel Uhl e60db6f90e Merge remote-tracking branch 'eclipse/main' into eclipse-main 2026-08-21 13:06:10 +02:00
Axel Uhl f5041c0255 (temporarily?) disabling YellowBrickAdapterTest;
The local / master-SNAPSHOT / master-latest / 19/08/2026 09:58 update on
their yb.tl site may have broken things for us. E-mail to their support
address has been sent, and we need to wait until we receive
clarification.
2026-08-21 13:04:09 +02:00
Axel UhlandCopilot 7304bae8a8 bug6286: fix IndexOutOfBoundsException in FixesAndTails.mergeFixes when switching tail color to a metric
In POLYCHROMATIC mode, Colorline.setPath(...) built zero polylines for a
single-vertex path, so getLength() returned 0 instead of 1. This broke the
FixesAndTails invariant tail.getLength() == lastShownFix - firstShownFix + 1,
after which mergeFixes computed out-of-range tail indices and threw an
IndexOutOfBoundsException while scrubbing the time slider.

Represent a one-vertex path as a single Polyline holding that vertex, matching
the interim single-vertex state insertAt builds up and getLength's case 1
already expects.

Assisted-By: Claude (claude-4.8-opus)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-20 22:33:06 +02:00
Axel Uhl 9169a8acb1 Merge remote-tracking branch 'eclipse/bug6281' into eclipse-main 2026-08-20 17:20:37 +02:00
Axel Uhl 0742f4ae3c fixed parameter naming for SailingService.createPairingListTemplate 2026-08-20 02:07:16 +02:00
Axel Uhl 66a0473882 bug6281: added doc and release notes 2026-08-19 14:39:01 +02:00
Axel Uhl 7b014add17 bug6281: first implementation of new /wind_at_position method on RegattasResource 2026-08-19 14:20:58 +02:00
Axel Uhl 2783921cfe Merge branch 'eclipse-main' into bug6281 2026-08-19 09:59:33 +02:00
Axel Uhl 113eac3cca hint for Claude/Copilot to add "Assisted-By: " to commit messages 2026-08-18 17:46:01 +02:00
Axel Uhl eff5ab78b1 added "zuerichsee" as WindFinder spot collection 2026-08-18 13:02:49 +02:00
Axel Uhl d94640e01b bug6281: removed redundant statement 2026-08-18 10:10:23 +02:00
Axel Uhl 5a883425fb show one decimal place in wind chart tool-tips 2026-08-14 14:39:08 +02:00
Axel Uhl 0ec690baa0 re-enabled NOAA tests 2026-08-13 19:50:42 +02:00
Axel Uhl a09a511884 temporarily (?) disable NOAA tests (again!) as NOAA seems down currently 2026-08-12 18:46:36 +02:00
Axel Uhl 1323791d57 Merge branch 'bug6275' into eclipse-main 2026-08-12 12:25:55 +02:00
Axel Uhl a1c316ff76 bug6275: release notes 2026-08-12 12:25:13 +02:00
Axel Uhl e6f047ed8f bug6275: small adjustment of help text for Authenticate dialog 2026-08-12 12:21:04 +02:00
Axel Uhl 2e860c4252 updated memory configuration for docker-based wind estimation 2026-08-11 19:15:27 +02:00
Axel Uhl d7f115b373 bug6275: also set "default suggestions" for course area suggest box/oracle 2026-08-07 19:22:05 +02:00
Axel Uhl 0e7e23880b Merge branch 'eclipse-main' into bug6275 2026-08-07 17:26:21 +02:00
Axel Uhl d7016e3b2d Merge branch 'translation' into eclipse-main 2026-08-07 13:51:57 +02:00
Axel Uhl 5e1d491556 Merge branch 'bug6241-opus-4.7' into eclipse-main 2026-08-07 13:47:05 +02:00
Axel Uhl 02be3803a5 added null-safety in maneuver calculations in case one mark of a two-mark waypoint does not have a position 2026-08-07 13:05:14 +02:00
Axel Uhl 4894898c55 bug6275: added comments 2026-08-05 15:28:47 +02:00
Axel Uhl 7b3c2bb4bc bug6275: always keep "Clear all" button enabled 2026-08-05 14:08:31 +02:00
Axel Uhl 174e8a0d59 bug6275: removed unused copyCourseAreasFromRemoteServer string message 2026-08-05 13:57:54 +02:00
Axel Uhl 1239533be3 bug6275: Javadoc replacing line comment on SailingServer interface 2026-08-05 13:38:28 +02:00
Axel Uhl 39427b9b5b bug6275: removed unused fields from EventDialog 2026-08-05 13:35:59 +02:00
Axel Uhl b2994fee62 bug6275: removed unused code 2026-08-05 13:34:51 +02:00
Axel Uhl 98dd7ba3f6 Merge branch 'eclipse-main' into bug6275 2026-08-05 13:30:23 +02:00
Axel Uhl 940070880d adjusted the docker image location for windestimation to ghcr.io 2026-08-05 13:17:37 +02:00
Axel Uhl 2db1f4af1d dockerfile for windestimation training now using temurin JDK 2026-08-05 13:07:50 +02:00
Axel Uhl b4cc019587 Merge branch 'eclipse-main' into bug6275 2026-08-05 10:44:38 +02:00
Axel Uhl 1f68dd2867 fixed UTF-8 encoding of German SAP branding messages 2026-08-05 10:38:52 +02:00
Axel Uhl eb068fa670 bug6241: add PolarDataService.raceRemoved to prune per-race loading callbacks
Follow-up to holding PolarDataMiner's race-keyed collections weakly. The weak
key on callbacksWaitingForFixIngestion was defeated by its own values: a parked
callback (the scheduleWindEstimationInstallation closure) strongly captures the
same TrackedRace that keys its entry, so the entry -- and the race with all its
tracks -- would never be collected while parked. That is a brittle contract to
place on callers (they must ensure their Runnable holds no strong reference to
the race), and easy to get wrong.

Instead:
 - callbacksWaitingForFixIngestion goes back to a strong HashMap (weak keys were
   misleading there) and is pruned explicitly.
 - racesWithIngestedFixes stays a weak set (it has no values, so weak keys are
   effective) and is additionally pruned eagerly as belt-and-suspenders.
 - New PolarDataService.raceRemoved(TrackedRace) (delegating to
   PolarDataMiner.raceRemoved) forgets both collections for a race.
 - RacingEventServiceImpl.RaceAdditionListener.raceRemoved now calls
   polarDataService.raceRemoved(trackedRace) (null-guarded, since the service
   arrives via an OSGi ServiceTracker).

The runWhenPolarLoadingFinishedFor Javadoc gains an explicit LEAK CONTRACT
paragraph documenting that callers must arrange for raceRemoved to be called on
race removal, and why the map cannot rely on weak keys. Dropping a removed
instance's parked install callback loses nothing: a re-added race is a new
TrackedRace instance that gets a fresh raceAdded -> scheduleWindEstimationInstallation
-> runWhenPolarLoadingFinishedFor registration. Post-startup manual loads still
wait for the loading-pipeline drain (Gate A on racesWithIngestedFixes ensures the
race's own fixes are ingested before the callback is eligible; the drain then
covers it and any concurrently-loading races). Live TRACKING fixes flow through
the separate preFilteringProcessor chain and never perturb the loading drain.

Also documents the bounded, self-clearing lifecycle of
callbacksWaitingForLoadingOfAllRacesToRestoreToStart.
2026-08-04 17:52:39 +02:00
Axel Uhl d8fc0689cf bug6241: hold PolarDataMiner race-tracking sets weakly to avoid a leak
racesWithIngestedFixes was a HashSet<TrackedRace> that was only ever added to
(in raceFinishedLoading), never removed. Every race ever loaded stayed in the
set for the lifetime of the miner -- i.e. the server process -- pinning the
TrackedRace and, transitively, all of its tracks. On a long-running ARCHIVE
server that loads tens of thousands of races (and reloads across the day), this
is a real, unbounded memory leak; removed races could never be freed.

There is no safe explicit removal point: a runWhenPolarLoadingFinishedFor call
for a race may legitimately arrive long after its fixes were ingested (e.g. a
wind-estimation factory-service swap reschedules the install per race via
scheduleWindEstimationInstallation), so the set cannot be cleared on drain
without risking a stranded, never-firing callback for a still-live race.

Instead hold the set weakly: Collections.newSetFromMap(new WeakHashMap<>()).
Entries evict once a race is no longer strongly reachable elsewhere, at which
point no further runWhenPolarLoadingFinishedFor call for it is possible, so
losing the 'already ingested' bit is harmless. Live races keep their entry and
the ingestion gate behaves exactly as before. All access remains under the
existing 'synchronized (callbacksWaitingForFixIngestion)' monitor, so the
non-thread-safe weak collections are safe.

callbacksWaitingForFixIngestion (also keyed by TrackedRace) is switched to a
WeakHashMap for the same reason: a race removed while still parked here (e.g.
removed while stuck in LOADING before ingestion) would otherwise be pinned
forever. Its entries for live races are still removed explicitly in
raceFinishedLoading.

Javadoc on both fields rewrapped to the project's 120-column comment width.
2026-08-04 17:08:09 +02:00
Axel Uhl a62df20bec bug6241: restore corrected wording in ci-test-environment.xml comment
Follow-up to a960398cd3, which inadvertently captured a half-edited state of
this file (the previous commit staged it while a local edit was in flight).
This restores the intended version: 'information'/'sites'/'Internet Explorer'
wording and the empty-line cleanup in the web-driver system-properties comment.
Comment/whitespace only; no test behavior change.
2026-08-04 16:04:56 +02:00
Axel Uhl a960398cd3 bug6241: clarify that runWhenPolarLoadingFinishedFor waits for a global drain
The Javadoc on PolarDataService.runWhenPolarLoadingFinishedFor (interface),
PolarDataMiner (impl) and the RacingEventServiceImpl.scheduleWindEstimationInstallation
caller all read as if the callback fired once the fixes belonging to the given
race had drained. In reality the loading pipeline (preFilteringProcessorForLoadedFixes)
is shared across all races and has no per-race completion tracking, so the
callback fires only on a global drain: when the fixes of every race ingested so
far have been processed.

The race parameter does not scope the wait; it only gates WHEN the callback may
attach to the shared drain, via two conditions that must both hold: (1) the
race's own fixes have been ingested (so the callback can't fire on a pipeline
that is merely momentarily idle before this race was queued), and (2)
markLoadingOfAllRacesToRestoreStarted() has been signalled (so an idle window
between two startup races' ingestion bursts isn't mistaken for a real drain).

This global-wait behavior is deliberate: we want the polar model to reflect all
loaded data before any maneuver-based wind estimation is installed, and we accept
the resulting startup sequentiality (an early race's estimator install waits
behind the whole loaded-fix backlog). Documentation only; no behavior change.
The method name is kept as-is.
2026-08-04 14:54:20 +02:00
Axel Uhl 413d7b298d bug6241: use @link instead of @code in Javadoc where possible 2026-08-04 13:52:38 +02:00
Axel Uhl ea57eb17ed bug6241: comment formatting 2026-08-04 13:27:05 +02:00
Axel Uhl c777b396b1 bug6241: comment formatting 2026-08-04 13:18:36 +02:00
Axel Uhl cc43993bca bug6241: better naming for constant, appending unit 2026-08-04 11:27:04 +02:00
Axel Uhl 598564f2c4 bug6241: moved field initialization into constructor 2026-08-04 11:05:12 +02:00
Axel Uhl 82b50e9f22 Merge branch 'eclipse-main' into bug6241-opus-4.7 2026-08-04 10:09:28 +02:00
Axel Uhl e2c1339ce0 Merge branch 'eclipse-main' into bug6275 2026-08-03 18:14:37 +02:00
Axel Uhl 5b9abd9698 Merge branch 'eclipse-main' into bug6241-opus-4.7 2026-08-01 20:14:22 +02:00
Axel Uhl 909c4ae386 bug6241: make ManeuverCacheDelegate.canBeUpdated reflect current inner cache
The delegate's canBeUpdated() was returning true unconditionally, hiding the
distinction between the DB-load state (cacheToUse = ManeuversFromDatabase,
inner canBeUpdated=false) and the compute state (cacheToUse =
ManeuversFromSmartFutureCache, inner canBeUpdated=true). That broke the
gate on TrackedRaceImpl.feedAlreadyKnownManeuversToWindEstimation which
uses maneuverCache.canBeUpdated() to decide whether the current JVM's
cache is DB-backed and therefore needs alreadyClassifiedManeuversAvailable
to feed the estimator.

With the pre-fix behavior, the gate never opened even for Path A races:
feed was always skipped, the estimator never received the loaded
maneuvers, PreClassifiedUpdate never ran, and the MBE wind track stayed
empty. Observable as hasWindData=false on all fingerprint-matched races
of "The Final Push 49erFX" (W1..W9, W11, W12) on archive-candidate even
28+ hours after the wind-estimation install callback had fired, while
production (running older code) had hasWindData=true.

Change canBeUpdated() to delegate to cacheToUse.canBeUpdated(). Now it
truthfully reflects which inner cache is in use. All other callers of
cacheToUse.canBeUpdated() within the delegate (ensureFilled, computeAndStore,
triggerUpdate) already read the inner cache directly and are unaffected.
The only external caller was TrackedRaceImpl.feedAlreadyKnownManeuversToWindEstimation
which now correctly opens the gate for Path A and keeps it closed for
Path B.

Javadoc on the method explains the semantics and how it toggles across
resume() and triggerUpdate() calls.
2026-08-01 20:13:21 +02:00
Axel Uhl f3da21c5f4 bug6278: harmonized listeners and pending action counting;
introduced abstract base class for AsyncActionsExecutor and
TimeRangeActionsExecutor
2026-07-31 23:33:54 +02:00
Axel Uhl 9470d78bbc bug6278: added observer pattern for TimeRangeActionsExecutor, too 2026-07-31 18:47:23 +02:00
Axel Uhl 17afe27cf9 bug6278: started with observing the asyncActionsExecutor and displaying pending actions count 2026-07-31 18:21:53 +02:00
Axel Uhl f87358e3f9 Removed temporary GWT.log statement from bug6150 2026-07-31 17:03:34 +02:00
Axel Uhl 80665b59e5 Merge branch 'eclipse-main' into bug6275 2026-07-31 14:37:26 +02:00
Axel Uhl 1fd78841af Merge remote-tracking branch 'github/eclipse-main' into eclipse-main 2026-07-31 14:23:37 +02:00
Axel Uhl 61393e0ca1 Merge branch 'bug6150' into eclipse-main 2026-07-31 14:16:46 +02:00
Axel Uhl 58a9656e99 fixed typo in comment 2026-07-31 10:57:16 +02:00
Axel Uhl e7a87d3b98 see https://github.com/SAP/sailing-analytics/pull/46;
making the link to GitHub Pages part of a branding configuration
2026-07-30 18:00:01 +02:00
Axel Uhl 0bed98cc61 aftermath to 9bf4537fd7: use https://sailing-analytics.eclipse.dev/ for upstream 2026-07-30 15:27:24 +02:00
Axel Uhl 3fd5556425 Merge remote-tracking branch 'eclipse/main' into eclipse-main 2026-07-30 15:20:36 +02:00
Axel Uhl 34c18c9e53 Merge branch 'eclipse-main' into bug6276 2026-07-30 13:54:11 +02:00
Axel Uhl 3a5929cbaa Merge remote-tracking branch 'eclipse/bug6276' into bug6276 2026-07-30 13:53:57 +02:00
Axel Uhl 54fd3a4683 bug6150: comments, release notes 2026-07-30 13:48:11 +02:00
Axel Uhl bf72f5b61d bug6150: use generic adding/dividing for ScalableValues 2026-07-30 13:41:42 +02:00
Axel Uhl 8d5ec12182 Merge branch 'eclipse-main' into bug6150 2026-07-30 10:13:45 +02:00
Axel Uhl 05b27621b3 bug6241: extend self-heal to detect all-UNKNOWN maneuver DB records
The existing isAllEmpty() self-heal in ManeuverCacheDelegate.resume() only
caught the case where every competitor's persisted maneuver list was null
or literally empty. A separate poisoned state slipped through: lists are
non-empty but every stored maneuver is typed as UNKNOWN (or PENALTY_CIRCLE),
which happens when the detector ran while getWind(pos, at) still returned
null everywhere and createManeuverFromManeuverCurveAndWind hit its 'wind ==
null' fall-through in ManeuverDetectorImpl.

Feeding such records to the estimator via alreadyClassifiedManeuversAvailable
produces zero wind fixes because
IncrementalMstHmmWindEstimationForTrackedRace.mapManeuverType maps UNKNOWN
and PENALTY_CIRCLE to null and the whole batch gets filtered out. Observable
as hasWindData=false on the affected races even after the wind estimation
callback has fired. Confirmed on archive-candidate for W10 of "The Final
Push 49erFX" (982 stored maneuvers, all UNKNOWN) and for Abeam Training
2016 Race 1 (453 stored, all UNKNOWN); production shows the same event with
hasWindData=true because it ran with older code paths that populated the
MBE track through a different route.

The fix adds a companion check, hasNoClassifiableManeuver, invoked after
isAllEmpty in resume(). It returns true iff no stored maneuver anywhere in
the loaded map is typed as TACK/JIBE/HEAD_UP/BEAR_AWAY (the four types
mapManeuverType turns into a non-null ManeuverTypeForClassification). When
it fires, resume() logs the reason and falls through to the compute path,
which now correctly sequences its storage after the wind estimator has
settled (see computeAndStore / retypeAndStoreAfterWindEstimationSettled) so
the recompute produces properly-typed maneuvers and overwrites the poison.

isClassifiableType() is a tiny helper listing the four classifiable types;
a comment reminds future maintainers to keep it in sync with mapManeuverType.
2026-07-29 19:38:58 +02:00
Axel Uhl f214114781 Merge remote-tracking branch 'eclipse/bug6150' into bug6150 2026-07-28 21:41:34 +02:00
Axel Uhl 8c56a2dff0 Merge remote-tracking branch 'eclipse/main' into eclipse-main 2026-07-28 21:40:03 +02:00
Axel Uhl c1e972370d bug6275: whitespace and comment changes only 2026-07-28 16:54:45 +02:00
Axel Uhl d66d2838aa Merge branch 'eclipse-main' into bug6275 2026-07-28 16:19:08 +02:00
Axel Uhl 868f211392 Merge branch 'eclipse-main' into bug6150 2026-07-28 15:22:29 +02:00
Axel Uhl 8362204354 new message strings for Sail Insight 2026-07-28 14:40:43 +02:00
Axel Uhl 165bc8f304 bug6241: gate feedAlreadyKnownManeuversToWindEstimation on !canBeUpdated
Restores the compute-path guard on feedAlreadyKnownManeuversToWindEstimation.
Without the gate, setWindEstimation submits per-competitor feed tasks that
call maneuverCache.get(waitForLatest=true) concurrently with the ordinary
detector-driven feed via newManeuverSpotsDetected. Depending on ordering the
feed task either sees the recalculation already in progress (blocks, then
double-feeds via alreadyClassifiedManeuversAvailable, whose per-competitor
PreClassifiedUpdate reconciliation clobbers the graph path's whole-race
track) or completes before the trigger fires (get returns null, feed skips).

The double-feed shows up as flakiness of
IncrementalMstHmmWindEstimationForTrackedRaceTest -- observed locally with a
ratio-of-matching-fixes of 0.658 vs the 0.75 threshold, and also seen on
GitHub Actions run 30262443673.

The rationale for removing the gate previously (that after the bug6241
polar-data hold-back the estimator may be installed after detection ran
with a null WindEstimationInteraction) is now handled explicitly by the
retype-and-store choreography in ManeuverCacheDelegate.computeAndStore:
its runWhenWindEstimationInstalled callback re-drives the detector once
the estimator is in place, and freshly built detectors (whose per-competitor
cache was cleared by updateManeuversAndWindWithNewWindEstimation at install
time) emit spots through the graph path with cross-competitor aggregation
intact -- no per-competitor slice ever needed.

Adds a substantial comment on the method spelling out why the gate must
stay in place, so future edits don't remove it again.
2026-07-27 21:17:53 +02:00
Axel Uhl 139ad492fb Merge branch 'eclipse-main' into bug6241-opus-4.7 2026-07-27 13:42:51 +02:00
Axel Uhl 86f4193930 make hudson.pyscript resilient against missing URL scheme 2026-07-27 13:33:38 +02:00
Axel Uhl 610df7c5a4 bug6241: two-round retype-and-store to actually see estimator wind fixes
The previous single-round retypeAndStoreAfterWindEstimationSettled was
incorrect: it called waitUntilDone on the estimator before any spots
had been re-emitted (the compute-path's initial detection ran with a
null WindEstimationInteraction because the estimator hadn't been
installed yet, so no spots were pushed). waitUntilDone then returned
immediately on an empty queue, and the subsequent recalculate emitted
spots but returned before the estimator could process them and produce
wind fixes. Net effect: stored maneuvers were still typed with null
wind (mostly UNKNOWN), reproducing the poisoned-DB pattern the fix was
supposed to prevent.

Split into two rounds:

  1. First recalculate + get per competitor. Since setWindEstimation
     cleared maneuverDetectorPerCompetitorCache, this builds a fresh
     detector with a non-null WindEstimationInteraction, runs a full
     scan, and *does* emit spots to the estimator during this pass.
  2. waitUntilDone -- now the estimator's queue has the spots from
     round 1 and this actually waits for the graph inference to
     produce wind fixes and flow them into the tracked race's
     MANEUVER_BASED_ESTIMATION track.
  3. Second recalculate + get per competitor. The detector's
     lastManeuverDetectionResult is now populated and no new raw fixes
     have arrived, so it takes the re-type branch of detectManeuverSpots
     -- which routes through determineManeuversFromManeuverCurve and
     types the spots using trackedRace.getWind(...), which now sees the
     estimator's fixes (bug6274 removed the MANEUVER_BASED_ESTIMATION
     exclusion). The isManeuverSpotWindNearlySame check correctly
     detects that the previous typing had null wind and the current
     has non-null, so it doesn't short-circuit to the UNKNOWN spot.
  4. Snapshot the classified maneuvers and store.

For W1 in 49erFX (5 competitors), pre-fix Mongo had 1 competitor with
362 typed maneuvers (whose detection happened to run after
setWindEstimation) and 1 with 628 all-UNKNOWN (whose detection ran
before setWindEstimation). Post-fix, both should be typed.
2026-07-26 23:39:36 +02:00
Axel Uhl 8ce464986b bug6241: detach retype-and-store callback onto its own thread
The Runnable passed to runWhenWindEstimationInstalled(...) fires
synchronously on whichever thread first invokes setWindEstimation with
a non-null argument. That thread is typically a shared
background-executor thread (the same pool used by SmartFutureCache and
the wind estimator's GraphRecalculationTask). Running waitUntilDone,
recalculate(...), and get(waitForLatest=true) on that thread makes the
callback block waiting for tasks that need the same pool to progress,
which starves the pool -- fatal with pool size 1, and observed as
timeout retries on saturated pools in local runs.

Detach the callback body onto a dedicated Thread so the setWindEstimation
caller returns immediately and the shared pool stays free to service the
tasks the retype-and-store step is waiting on.
2026-07-26 22:40:05 +02:00
Axel Uhl d3795248ea bug6241: sequence maneuver-cache persistence after wind estimator settles
The previous ManeuverCacheDelegate.resume() Path B stored the initial
detector output before the wind estimator had a chance to produce any
fixes. For races without another wind source (typically no-race-start
practice sessions with only GPS tracks), that first pass typed every
spot as UNKNOWN or produced no maneuvers at all, and the empty result
was persisted. On subsequent server starts the fingerprint matched, an
empty maneuver list got loaded, feedAlreadyKnownManeuversToWindEstimation
had nothing to hand to the estimator, and no wind fixes materialised.

Two fixes:

 * New primitive TrackedRace.runWhenWindEstimationInstalled(Runnable),
   default fires synchronously if getWindEstimation() is already
   non-null else no-op (suitable for mocks). TrackedRaceImpl overrides
   with the full callback-registry pattern used by runWhenPastLoading
   (two-cooperating-listeners, CAS-guarded single fire, race-removal
   cancellation via regatta listener). Two new transient fields on
   TrackedRaceImpl for the callback list and its monitor, both
   reinitialized in readObject for replicas.

 * ManeuverCacheDelegate.resume() refactored into three methods --
   resume(), computeAndStore(), retypeAndStoreAfterWindEstimationSettled().
   The compute path now runs the detector, then via
   runWhenWindEstimationInstalled(...) waits until the estimator is
   installed, awaits its inference via waitUntilDone(), recalculates
   each competitor so maneuvers are re-typed using the estimator's
   MANEUVER_BASED_ESTIMATION fixes (visible to typing thanks to bug6274),
   then snapshots and persists. The DB now captures the converged
   classification, not the pre-estimator empty state.

resume() also self-heals against DB records poisoned by earlier server
runs: on fingerprint match with all-empty stored maneuvers, treats as
a stale cache miss and falls through to compute. Races that legitimately
have no maneuvers (short races with no motion) will recompute on every
startup, which is acceptable because the cost is proportional to zero.
2026-07-26 21:39:11 +02:00
Axel Uhl 60403c7acb Merge branch 'eclipse-main' into bug6241-opus-4.7 2026-07-26 08:28:15 +02:00
Axel Uhl 9aeac83bc1 use MarkedAsyncCallback in AbstractRefresher to support Selenium tests;
we saw several test failures in classes like AclRevokeAnonymousTest, such as
https://hudson.sapsailing.com/job/main/8187/testReport/junit/com.sap.sailing.selenium.api.test/AclRevokeAnonymousTest/test___Selenium_Test___com_sap_sailing_selenium_core_HeadlessFirefoxDriver/.
They were most likely caused by a GWT RPC call not returning before the
test continued to look for the results of those calls.
2026-07-26 07:54:04 +02:00
Axel Uhl c46e1d56f9 Merge branch 'eclipse-main' into bug6241-opus-4.7 2026-07-25 14:15:42 +02:00
Axel Uhl aac6aa67a0 Merge branch 'bug6277' into eclipse-main 2026-07-25 14:15:18 +02:00
Axel Uhl 9121826fe7 Merge branch 'eclipse-main' into bug6241-opus-4.7 2026-07-25 09:32:33 +02:00
Axel Uhl fdb8166dd9 release notes for new instance type 2026-07-25 09:17:09 +02:00
Axel Uhl e045d0ee15 switching default shared instance type for MongoDB and shared instances to I4_I_2_XLARGE 2026-07-25 09:15:44 +02:00
Axel Uhl ffc214ef87 bug6277: Javadoc 2026-07-25 08:56:51 +02:00
Axel Uhl 4e1a406b99 bug6277: adjusting course middle line tooltip upon each round trip 2026-07-25 08:55:09 +02:00
Axel Uhl c419c67d5a Merge branch 'eclipse-main' into bug6241-opus-4.7 2026-07-24 08:47:28 +02:00
Axel Uhl 2a120f9ecb fixing bearer token duplication for TracTrac course design update handler 2026-07-24 08:31:42 +02:00
Axel Uhl 9fab6ed469 fixed dynamic update for course middle line 2026-07-22 10:55:22 +02:00
Axel Uhl 29b009ebd1 bug6275: using landscape constants 2026-07-22 09:25:50 +02:00
Axel Uhl cfa8916a2f bug6275: made compilable 2026-07-22 08:52:59 +02:00
Axel Uhl cabe62705b Merge remote-tracking branch 'eclipse/bug6275' into bug6275 2026-07-22 08:44:16 +02:00
Axel Uhl a8186ee9a0 Merge branch 'eclipse-main' into bug6275 2026-07-22 08:44:11 +02:00
Axel Uhl 34394d901b Merge remote-tracking branch 'github/eclipse-main' into eclipse-main 2026-07-21 20:23:00 +02:00
Axel Uhl 43279c843a bug6241: polars bundle registers PolarDataService once, fully initialized
Previously the polars bundle registered its PolarDataService in the OSGi
registry immediately at bundle start, then unregistered/re-registered
around a remote regression-data fetch in a background thread. The
unregister transiently nulled RacingEventServiceImpl.polarDataService,
opening a window in which PolarFixCacheUpdater.statusChanged would skip
its raceFinishedLoading call for any race transitioning past LOADING
during that window, and in which restoreTrackedRaces()'s call to
markLoadingOfAllRacesToRestoreStarted() would silently drop.

Reversed the dependency: the polars Activator now opens a ServiceTracker
for RacingEventService, obtains its DomainFactory, constructs the
PolarDataServiceImpl, populates it (registerDomainFactory, then remote
fetch if configured), and only then registers the service in the OSGi
registry -- exactly once. The OSGi contract now holds: if the service
is registered, it is ready.

Also dropped the !maneuverCache.canBeUpdated() gate on
TrackedRaceImpl.feedAlreadyKnownManeuversToWindEstimation, so that
compute-path races whose maneuver detector captured a null
WindEstimationInteraction at detection time (because the estimator
hadn't been installed yet) still get their detected maneuvers fed to
the estimator when it arrives.

Manifest change: com.sap.sailing.polars now Require-Bundles
com.sap.sailing.server.interface directly, in order to reference the
RacingEventService type from the Activator's ServiceTracker.
2026-07-21 19:15:42 +02:00