Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
9ed9b95
Publish the performance results through the shared step
jwrosewell Oct 26, 2026
c26ca03
Test that a failed poll keeps the licence key out of its error
jwrosewell Oct 26, 2026
d1d9449
Keep the licence keys out of the data update errors
jwrosewell Oct 26, 2026
a8cf16b
Never store or serve a cached response that carries a 51Did
jwrosewell Oct 26, 2026
363a966
Write the response cache key so no value can stand in for another field
jwrosewell Oct 26, 2026
2b32fc5
Test that each poll is delayed at random
jwrosewell Oct 26, 2026
46d5a88
Delay every poll by a random amount, drawn afresh each time
jwrosewell Oct 26, 2026
e0522c8
Test the file watcher's lifetime, master switch and file filter
jwrosewell Oct 26, 2026
b69f485
Decide promise and fetch support for each request on its own
jwrosewell Oct 26, 2026
427690a
Release a watched engine with its service, run the watcher only with …
jwrosewell Oct 26, 2026
4751882
Test that a refresh leaves an engine as one built on the new data file
jwrosewell Oct 26, 2026
1522af3
Follow the new data file's properties, tier and publish date on refresh
jwrosewell Oct 26, 2026
276dd89
Test that a failed update on start-up leaves the engine on its data file
jwrosewell Oct 26, 2026
05ece20
Put the flow errors in the JSON as a list of messages
jwrosewell Oct 26, 2026
e97ff32
Keep the local data file when the update on start-up fails, and regis…
jwrosewell Oct 26, 2026
bfa4225
Send no ETag with the response to a request that recorded errors
jwrosewell Oct 26, 2026
9cf4dcd
Report a failed cloud request once, not again as a configuration error
jwrosewell Oct 26, 2026
5fc06a9
Process a flow data once
jwrosewell Oct 26, 2026
cfb7094
Name each error in the aggregate processing error's message
jwrosewell Oct 26, 2026
7637de0
Treat a sequence with no next integer as a new sequence
jwrosewell Oct 26, 2026
7783f1c
Let a usage sharing add timeout too long for the clock wait for room
jwrosewell Oct 26, 2026
34cc8d6
Wait for usage sharing records without a time limit
jwrosewell Oct 26, 2026
04a352e
Benchmark this checkout's crates, not the published ones
jwrosewell Oct 26, 2026
42cef90
Read a device id only as far as the slice holding it
jwrosewell Oct 26, 2026
e92d0d5
Lay out the IP Intelligence collection item as the C structure is
jwrosewell Oct 26, 2026
54aa0bd
Log the errors a suppressing pipeline records
jwrosewell Oct 26, 2026
7ad8e2d
Build the usage sharing HTTP client on the element's own thread
jwrosewell Oct 26, 2026
8716d71
Take the licence keys out of every data update error by value, as the…
jwrosewell Oct 27, 2026
44ce4d3
Test that a download is written to disk as it arrives and cleaned up …
jwrosewell Oct 26, 2026
5168f3b
Write a data update to disk as it arrives, and remove its temporary f…
jwrosewell Oct 26, 2026
96ff1cb
Stop a download when the service is dropped, and say what temp_data_d…
jwrosewell Oct 27, 2026
951a841
Print how many delays differ, and format
jwrosewell Oct 27, 2026
509640e
Say in the update example that the watcher needs automatic updates, a…
jwrosewell Oct 27, 2026
fb78924
Format the refresh tests
jwrosewell Oct 27, 2026
69aab25
Give the same evidence the same ETag in every process
jwrosewell Oct 26, 2026
5163e88
Test that an update replaces the data file of a running engine under …
jwrosewell Oct 26, 2026
59bc279
Read a copy of the data file while updates can replace it
jwrosewell Oct 26, 2026
a205eae
Format the operational copy tests
jwrosewell Oct 27, 2026
a985500
Record flate2 as a test dependency of the IP Intelligence engine in t…
jwrosewell Oct 27, 2026
6b4c848
Count only service trouble towards a recovery period
jwrosewell Oct 27, 2026
31e6eec
Name the copy each loaded data set holds for its drop alone, as nothi…
jwrosewell Oct 27, 2026
96c02d1
Wait for a free file handle rather than fail when a manager is shared
jwrosewell Oct 26, 2026
d23a368
Free native exception messages with the library's own free function
jwrosewell Oct 27, 2026
cdc658c
Pass a data file path in the form the native libraries open it
jwrosewell Oct 27, 2026
5f37036
Integrate #158
jwrosewell Oct 27, 2026
b96f5b2
Integrate #173
jwrosewell Oct 27, 2026
73791ae
Integrate #174
jwrosewell Oct 27, 2026
ffbe4d9
Integrate #175
jwrosewell Oct 27, 2026
263c9af
Integrate #176
jwrosewell Oct 27, 2026
801d071
Integrate #177
jwrosewell Oct 27, 2026
9710888
Integrate #178
jwrosewell Oct 27, 2026
bcd7fbb
Integrate #179
jwrosewell Oct 27, 2026
913b956
Integrate #180
jwrosewell Oct 27, 2026
3ecac06
Integrate #181
jwrosewell Oct 27, 2026
3c2e616
Integrate #182
jwrosewell Oct 27, 2026
d553c47
Integrate #184
jwrosewell Oct 27, 2026
8410959
Integrate #185
jwrosewell Oct 27, 2026
e8ba2bf
Integrate #186
jwrosewell Oct 27, 2026
b8ae497
Integrate #187
jwrosewell Oct 27, 2026
0be97a7
Integrate #188
jwrosewell Oct 27, 2026
adfad7e
Integrate #189
jwrosewell Oct 27, 2026
2d217ac
Integrate #190
jwrosewell Oct 27, 2026
33489ad
Integrate #191
jwrosewell Oct 27, 2026
66fae61
Integrate #193
jwrosewell Oct 27, 2026
aa4fdd9
Integrate #194
jwrosewell Oct 27, 2026
482d916
Integrate #195
jwrosewell Oct 27, 2026
2c3bc63
Integrate #196
jwrosewell Oct 27, 2026
f74ae36
Integrate #198
jwrosewell Oct 27, 2026
71afdd6
Integrate #199
jwrosewell Oct 27, 2026
e3893c5
Integrate #200
jwrosewell Oct 27, 2026
fd4be55
Integrate #201
jwrosewell Oct 27, 2026
8774b69
Integrate #202
jwrosewell Oct 27, 2026
272a602
Wrap the sequence after the largest integer, as .NET and Java do
jwrosewell Oct 27, 2026
082ae06
Take up to twice the minimum entries within the take timeout
jwrosewell Oct 27, 2026
44ca522
Let the builder name the folder the data file copy is made in
jwrosewell Oct 27, 2026
2b0a5fe
Merge branch 'fix/sequence-overflow' into sweep/integration-4.5.12
jwrosewell Oct 27, 2026
0cb1809
Merge branch 'fix/share-usage-idle-wait' into sweep/integration-4.5.12
jwrosewell Oct 27, 2026
10d498f
Merge branch 'fix/operational-data-copy' into sweep/integration-4.5.12
jwrosewell Oct 27, 2026
82a50a3
BUILD: Rename the common-cxx mutex functions in the IP Intelligence copy
jwrosewell Oct 27, 2026
d6fd0f9
TEST: Keep the threaded read test off WebAssembly
jwrosewell Oct 27, 2026
ca1bf02
TEST: Keep the IP Intelligence threaded read test off WebAssembly
jwrosewell Oct 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 18 additions & 25 deletions .github/workflows/nightly-performance.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
name: Nightly Performance

# Runs the on-premise performance examples, writes their throughput figures into
# results_<name>.json in the common-ci schema, and uploads them so the shared
# steps/compare-performance.ps1 can chart them on the documentation performance
# graphs. The Rust on-premise engines call the device-detection-cxx and
# ip-intelligence-cxx libraries through FFI, so the figures track native
# performance.
# Runs the on-premise performance examples against this checkout's crates. Each
# example writes its results JSON, the shared
# steps/publish-performance-results.ps1 validates it and puts it at
# results_<name>.json, and the shared steps/compare-performance.ps1 charts it on
# the documentation performance graphs. The Rust on-premise engines call the
# device-detection-cxx and ip-intelligence-cxx libraries through FFI, so the
# figures track native performance.
#
# This mirrors the per-language performance steps in common-ci's nightly
# workflows. It is kept self-contained here (rather than calling the shared
Expand Down Expand Up @@ -56,14 +57,14 @@ jobs:
with:
path: rust
submodules: recursive

# 2. common-ci, checked out for the shared comparison step
# (steps/compare-performance.ps1). The Rust performance adapter itself
# now lives in this repo at ci/run-performance-tests.ps1 (see
# ci/missing-ci-scripts.md), because it serves only this repository and
# a folder named rust in common-ci collides with the clone of a
# repository named rust (51Degrees/rust#50); only the comparison step
# is shared.
# The adapter versions the crates from the release tags.
fetch-depth: 0

# 2. common-ci, checked out for the shared steps that publish and compare
# the results (steps/publish-performance-results.ps1 and
# steps/compare-performance.ps1). The Rust performance adapter lives in
# this repo at ci/run-performance-tests.ps1 (see
# ci/missing-ci-scripts.md), because it serves only this repository.
- name: Checkout common-ci
uses: actions/checkout@v4
with:
Expand Down Expand Up @@ -132,8 +133,9 @@ jobs:
with:
workspaces: rust

# 5. Run the performance examples and write results_<name>.json files under
# rust/test-results/performance-summary in the common-ci schema.
# 5. Run the performance examples against this checkout's crates and
# publish each results_<name>.json under
# rust/test-results/performance-summary through the shared step.
- name: Run performance and write results
shell: pwsh
run: ./rust/ci/run-performance-tests.ps1 -RepoName rust
Expand Down Expand Up @@ -183,15 +185,6 @@ jobs:
@{ Name = "DeviceDetection-OnPremise"; RunPerformance = $true },
@{ Name = "IpIntelligence-OnPremise"; RunPerformance = $true }
)
# The performance run resolves examples/Cargo.lock against source.toml
# (the local-path patch), which rewrites this tracked lockfile. The
# shared compare step publishes graphs by switching to an orphan images
# branch, and git aborts that switch while a tracked file has
# uncommitted changes. Restore the committed lockfile so the tree is
# clean before the switch. Only this one file is touched by the run;
# git's "changes would be overwritten" error enumerates every colliding
# tracked file, and it lists only examples/Cargo.lock.
git -C rust checkout -- examples/Cargo.lock
./common-ci/steps/compare-performance.ps1 `
-RepoName 'rust' `
-OrgName '51Degrees' `
Expand Down
9 changes: 6 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,12 @@ is ignored by git and listed in the `fodid` manifest's `include`, so
the bundled asset and `UPSTREAM_PINNED_REF` to the same commit in one pull
request.
- `nightly-performance.yml` — nightly (and on-demand) on-premise throughput
run that feeds the documentation performance graphs. Its Rust adapter lives
in this repo at `ci/run-performance-tests.ps1`; only the shared comparison
step (`compare-performance.ps1`) comes from common-ci. See
run that feeds the documentation performance graphs. Its adapter,
`ci/run-performance-tests.ps1`, runs the performance examples against this
checkout's crates through `ci/test-examples-against-source.ps1`. Each
example writes its own results JSON, and common-ci provides the steps that
validate and compare it (`steps/publish-performance-results.ps1` and
`steps/compare-performance.ps1`). See
[ci/missing-ci-scripts.md](ci/missing-ci-scripts.md) for how this relates to
the org-standard reusable-workflow contract and the planned direction.

Expand Down
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,12 @@ path, so cloud-only users and most of CI build without a C compiler.
`.data_update_license_keys(..)`, and that refreshes the engine when the data
file changes on disk. The Distributor serves nothing without a licence key,
so a build with polling on and no key fails with an error that says so, as
it does in .NET, and a deployment of the free Lite file turns polling off
with `.auto_update(false)`. `.file_system_watcher(false)` turns the watcher
off, and `pipeline.resource::<DataUpdateService>()` reaches the service for
a check on demand.
it does in .NET, and a deployment of the free Lite file turns automatic
updates off with `.auto_update(false)`, which stops the polling and the file
watcher together, as the specification's master switch does.
`.file_system_watcher(false)` turns only the watcher off, and
`pipeline.resource::<DataUpdateService>()` reaches the service for a check
on demand.
- JavaScript minification in `fiftyone-javascript-builder` is opt-in behind the
`minify` feature and off by default. See ARCHITECTURE.md for why.

Expand Down
13 changes: 5 additions & 8 deletions ci/missing-ci-scripts.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ Rust consumer, so there is nothing to share. Every other language keeps its
- Repoints `nightly-performance.yml` to run `./rust/ci/run-performance-tests.ps1`
(the workflow checks this repo out into a `rust/` subdirectory) instead of
`./common-ci/rust/run-performance-tests.ps1`.
- Keeps the `Checkout common-ci` step, because the comparison step
`steps/compare-performance.ps1` is genuinely shared and still comes from
common-ci.
- Keeps the `Checkout common-ci` step, because the steps that publish and
compare the results, `steps/publish-performance-results.ps1` and
`steps/compare-performance.ps1`, are shared and come from common-ci.

Moving the adapter's home is the structural part of the change. Alongside it,
this repo's copy has since diverged from the common-ci original with
Expand All @@ -71,11 +71,8 @@ repository-specific fixes (the ASN data fetch, the `dryrun` input, the
fail the job), so the behaviour of the nightly run is not identical to the old
`common-ci/rust` path.

The copy in `common-ci/rust/run-performance-tests.ps1` is intentionally **left
in place** for now and will be removed in a separate, manually raised common-ci
PR. This repo's copy is the one CI uses, and it is now the source of truth: the
two copies are no longer identical, so the common-ci copy should not be edited
in place — it is only awaiting deletion.
common-ci no longer carries a `rust` folder, so this repo's copy is the only
one.

## The remaining gap (Phase 2, deferred)

Expand Down
116 changes: 42 additions & 74 deletions ci/run-performance-tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@ param(
[string]$RepoName = ".",
# Where the results files are written, relative to the repo directory. This is
# the path the nightly workflow uploads and the compare-performance step reads.
[string]$OutputDir = "test-results/performance-summary"
[string]$OutputDir = "test-results/performance-summary",
# The common-ci checkout whose steps/publish-performance-results.ps1
# validates each results file and copies it to $OutputDir. CI checks it out
# beside this repository.
[string]$CommonCi = (Join-Path $RepoName "../common-ci")
)
$ErrorActionPreference = "Stop"
$PSNativeCommandUseErrorActionPreference = $true

# Checked first, so a missing checkout fails before the long build.
$publishStep = Join-Path $CommonCi "steps/publish-performance-results.ps1"
if (!(Test-Path -Path $publishStep -PathType Leaf)) {
Write-Error "'$publishStep' was not found. Check out 51Degrees/common-ci beside this repository, or pass -CommonCi."
}

# The ip-intelligence-cxx checkout is a sibling of the repo directory (CI
# checks this repo out into $RepoName next to it; a local run passes "." from
# the repo root, whose parent is the workspace holding the sibling checkout).
Expand Down Expand Up @@ -39,83 +49,41 @@ try {
Pop-Location
}

# Runs the 51Degrees Rust on-premise performance examples in release and writes
# their throughput figures into results_<Name>.json files, in the same
# `{ HigherIsBetter = @{ metric = value } }` shape the shared
# steps/compare-performance.ps1 consumes. The Rust on-premise engines call the
# Runs the 51Degrees Rust on-premise performance examples in release. Each
# example writes its own results JSON, in the shape the shared
# steps/compare-performance.ps1 reads, and the shared
# steps/publish-performance-results.ps1 validates it and copies it to
# results_<Name>.json under $OutputDir. The Rust on-premise engines call the
# device-detection-cxx and ip-intelligence-cxx libraries through FFI, so the
# figures track the native C/C++ performance.
#
# Name is the configuration the graph history is keyed on, and the metric
# names inside each file are its series, so neither may change.
$performanceExamples = @(
@{ Name = "DeviceDetection-OnPremise"; Package = "device-detection-examples"; Bin = "dd-onprem-performance" }
@{ Name = "IpIntelligence-OnPremise"; Package = "ip-intelligence-examples"; Bin = "ipi-onprem-performance" }
)

Push-Location $RepoName
try {
$summaryDir = Join-Path (Get-Location) $OutputDir
New-Item -ItemType Directory -Force -Path $summaryDir | Out-Null

# The example crates live in their own workspace under examples/ and depend
# on the published crates from crates.io by default. The nightly must
# benchmark this checkout's engine code, not the released packages, so the
# cargo commands run from examples/ with `--config source.toml`, the patch
# file that points every fiftyone-* dependency at its local path.
$examplesDir = Join-Path (Get-Location) "examples"
# Absolute, because the examples run from the examples directory.
$resultsDir = Join-Path (Resolve-Path $RepoName).Path "test-results/performance"
foreach ($example in $performanceExamples) {
$example.Results = Join-Path $resultsDir "results_$($example.Name).json"
# A file left by an earlier run would otherwise pass for this run's figures.
Remove-Item -Path $example.Results -Force -ErrorAction SilentlyContinue
}

# Run one performance example and parse the throughput it prints. The
# examples take the highest throughput figure they report (the multi-threaded
# pass), which is the headline number for the product.
function Get-Throughput {
param(
[Parameter(Mandatory)][string]$Package,
[Parameter(Mandatory)][string]$Bin,
[Parameter(Mandatory)][string]$Pattern
)
Write-Host "Running performance example '$Bin'..."
Push-Location $examplesDir
try {
# The native exit code check is turned off for this one call so the
# example output is printed before any failure is raised. With it on,
# a non-zero cargo exit threw at the assignment, Write-Host below
# never ran, and the job log showed only that cargo ended
# with a non-zero exit code, with nothing the example or the
# compiler had said. That is what hid why ipi-onprem-performance was
# failing every night.
$PSNativeCommandUseErrorActionPreference = $false
$output = cargo run --release --config source.toml -p $Package --bin $Bin 2>&1 | Out-String
$exitCode = $LASTEXITCODE
} finally {
Pop-Location
}
Write-Host $output
if ($exitCode -ne 0) {
Write-Error "'$Bin' exited with code $exitCode. Its output is above."
}
$found = [regex]::Matches($output, $Pattern)
if ($found.Count -eq 0) {
Write-Error "Could not parse a throughput figure from '$Bin' output"
}
# Take the last match so the multi-threaded figure wins where an example
# prints both a single- and a multi-threaded result.
return [double]$found[$found.Count - 1].Groups[1].Value
# The examples depend on the published crates. ci/test-examples-against-source.ps1
# points them at this checkout for the run and puts the manifests and the lock
# back afterwards, so the figures are for the code being tested and not for the
# last release. The closure carries the list above into that script.
$runExamples = {
foreach ($example in $performanceExamples) {
Write-Host "Running performance example '$($example.Bin)'..."
cargo run --release --config source.toml -p $example.Package --bin $example.Bin -- --json-output $example.Results
}
}.GetNewClosure()
& (Join-Path $PSScriptRoot "test-examples-against-source.ps1") -Command $runExamples

# Device Detection on-premise (Hash): detections per second.
$ddDetectionsPerSecond = Get-Throughput `
-Package "device-detection-examples" `
-Bin "dd-onprem-performance" `
-Pattern "Detections per second\s*:\s*(\d+)"
@{ HigherIsBetter = @{ DetectionsPerSecond = $ddDetectionsPerSecond } } |
ConvertTo-Json -Depth 5 |
Out-File (Join-Path $summaryDir "results_DeviceDetection-OnPremise.json") -Encoding utf8

# IP Intelligence on-premise (IP graph): lookups per second.
$ipiLookupsPerSecond = Get-Throughput `
-Package "ip-intelligence-examples" `
-Bin "ipi-onprem-performance" `
-Pattern "Throughput:\s*(\d+)\s*lookups/sec"
@{ HigherIsBetter = @{ LookupsPerSecond = $ipiLookupsPerSecond } } |
ConvertTo-Json -Depth 5 |
Out-File (Join-Path $summaryDir "results_IpIntelligence-OnPremise.json") -Encoding utf8

Write-Host "Wrote performance results to '$summaryDir':"
Get-ChildItem $summaryDir -Filter "results_*.json" | ForEach-Object { Write-Host " - $($_.Name)" }
} finally {
Pop-Location
foreach ($example in $performanceExamples) {
& $publishStep -SourceFile $example.Results -Name $example.Name -RepoName $RepoName -SummaryDir $OutputDir
}
15 changes: 11 additions & 4 deletions ci/test-examples-against-source.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ The examples depend on the published crates, and examples/source.toml patches
each one to its path in this tree. Cargo uses a patch only when its version is
the one it would pick, so this first writes a version above every published
release into the crates, then moves each patched crate in the examples' lock to
the copy in this tree, then checks the lock and runs the tests.
the copy in this tree, then checks the lock and runs the command, which by
default builds and tests every example.

The manifests and the lock are put back as they were when the script ends, so a
run leaves the working tree unchanged.
Expand All @@ -20,10 +21,16 @@ such as 4.5.9 after 4.5.8.

.PARAMETER Root
The workspace root. Defaults to the repository this script sits in.

.PARAMETER Command
What to run in examples/ while the crates point at this tree. Each cargo
command in it needs `--config source.toml`. ci/run-performance-tests.ps1 passes
the performance examples here, so the nightly figures are for this tree.
#>
param (
[string]$Version,
[string]$Root = (Join-Path $PSScriptRoot "..")
[string]$Root = (Join-Path $PSScriptRoot ".."),
[scriptblock]$Command = { cargo test --config source.toml }
)
$ErrorActionPreference = "Stop"
$PSNativeCommandUseErrorActionPreference = $true
Expand All @@ -41,7 +48,7 @@ if (-not $Version) {
}
$Version = "$($released.Major).$($released.Minor).$($released.Build + 1)"
}
Write-Output "Testing the examples against this tree's crates at $Version."
Write-Output "Running the examples against this tree's crates at $Version."

$examples = Join-Path $Root "examples"
$patched = Get-Content -Path (Join-Path $examples "source.toml") |
Expand Down Expand Up @@ -94,7 +101,7 @@ try {
throw "These crates still come from crates.io, not this tree:`n$($fromRegistry -join "`n")"
}

cargo test --config source.toml
& $Command
} finally {
Pop-Location
foreach ($path in $originals.Keys) {
Expand Down
7 changes: 7 additions & 0 deletions cloud-request-engine/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,10 @@ pub const PROCESS_STARTED_KEY: &str = "process-started";
/// The element-data field holding the advisory warning messages, if any, that
/// the cloud service returned in the response's `warnings` array.
pub const WARNINGS_KEY: &str = "warnings";

/// The top-level member of a data response that carries 51Did values.
///
/// The cloud creates each 51Did afresh for the request that asked for it, so a
/// response carrying this member answers that one request and is never shared
/// with another through the response cache.
pub const IDENTIFIER_MEMBER: &str = "fodid";
Loading
Loading