Repository navigation
Make the data update follow the specification - #79
Merged
Merged
Conversation
…nce key check The tests read the messages and levels from the Logging section of the data update specification, check that update on start-up works with auto update off and that the file system watcher follows the auto update switch, and that an engine whose update URL is the Distributor's is refused without a licence key. Tests that built an engine with the defaults now switch auto update off or give a key, as the specification's defaults table says.
Send the data file's last modified time as If-Modified-Since when the file has no publication date. Register the data file with the update service when update on start-up or auto update is on, and let the auto update switch govern the file system watcher. Log the actions and errors with the messages and levels in the Logging section of the data update specification. Refuse to build with update on start-up or auto update on, the Distributor's URL and no licence key, naming the option to turn off.
The Logging section of the data update specification makes a failed download a warning, with the error detail from the server.
This was referenced Nov 9, 2026
The builder gives the data file its engine's type name, taken from the builder's own name, and the update service logs it until the engine is set.
The conflict in DataUpdateServiceDefault.downloadFile keeps this draft's messages, which are the specification's texts, and rc's masked URL for the debug line. The next commits mask the license keys in the draft's messages.
…omparison and the license key check across data files - DataUpdateServiceLoggingTests gives the data file a license key in its URL and checks that every message, at every level, and every exception logged with one, shows only the key's last 4 characters, for a 304, a 200, a failed connection, a failed download, a 403 and no response. - A failure writing the temporary file logs the Errors table's text for a write error. - With automatic update off and the file system watcher flag at its default, a programmatic update still compares the file on disk. - A builder with two data files, the second without a license key, refuses the build before it registers either.
The data update URL carries the license keys in its query. The messages for checking, downloading, a 304 reply, a failed connection and a failed download now name the URL with each key masked to its last 4 characters, and the error detail and the exception logged with a failure are masked too, because an HTTP library's exception often names the URL. The helper that named a data file by its engine for the old messages is no longer used and is removed.
A failure while writing the downloaded data to the temporary file was logged as the "Error downloading file" warning. The download stream now records whether a read failed, so a failure that is not a read logs the Errors table's "Error writing to data file location or temp file location" text, "An error occurred when writing to <file path>. Error detail: <error>", at error level, and returns AUTO_UPDATE_ERR_READING_STREAM, whose documentation covers a failed output stream. A failed read is still the download warning.
…r exists checkForUpdate skipped the comparison of the data file with the temporary file whenever the file system watcher flag was on. The watcher is created only when automatic update is on, so with automatic update off and the flag at its default, a programmatic update never looked at the file on disk. The data update specification's Programmatic update section says the file at the configured location is used "irrespective of whether Automatic Update from File is enabled". A programmatic update now always compares the files, and the timer compares them whenever no watcher exists, rather than whenever the flag is off.
preCreateEngine checked each data file's update service and license key inside the loop that registers the data files, so a builder with several data files could register the first, and run its update on start-up, before refusing the build for a later one. Every data file is now checked first, and a refused build registers none.
This was referenced Nov 10, 2026
…efresh DataUpdateService_CheckForUpdate_FileRefreshFails_TimerSetAgain, which rc added with the change that refreshes the engine once for an in-memory update, allowed for a warning on each of the 10 refresh tries. This draft logs each try at debug and one error after the last, the Errors table's "An error occurred while applying a data file update to <Engine type name>" row, so the test now allows that one error. The full build of the merge failed on it.
…-the-specification
data-updates.md gives two rows for AUTO_UPDATE_REFRESH_FAILED, being "An error occurred when applying a data update to engine '<Engine type name>' after <tries> tries." for the file, which is tried 10 times, and the same without the tries for data held in memory.
A refresh from the file, tried 10 times, logs "An error occurred when applying a data update to engine '<Engine type name>' after 10 tries.", and a refresh from data held in memory logs the same without the tries, as data-updates.md's two AUTO_UPDATE_REFRESH_FAILED rows give them, and as .NET and Go word them. The exception is still passed to the logger.
…-the-specification
…-the-specification
This was referenced Nov 11, 2026
Merged
Use the standard licence text in one LICENSE, and take the schema from configuration-validation
#109
Merged
Draft
Take the data update messages and levels .NET and 51degrees-node write
51Degrees/51degrees-python#98
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
pipeline/Gaps-vs-spec.mdlists where the Java pipeline differed from the specification. Checked against the code onrcand against the data update page, four data update items are still true:DataUpdateServiceDefaultsent the epoch asIf-Modified-Sincewhen the data file had no published date.OnPremiseAspectEngineBuilderBase.preCreateEngineregistered the data file with the update service only when auto update was on, soupdateOnStartupwithautoUpdateoff did nothing.The same checks found D2, D4 and the programmatic update already met, and D5, D7 and D9 not required by the text, so those are not touched. The usage sharing items (U1 to U5) wait for the record format decision and are not touched either.
The specification is quoted at commit ba079423 of
specificationsrc.What changes
Each change cites the specification sentence it follows.
If-Modified-Sinceis the data file's published date, or its last modified time on disk when it has none. Update polling: "If the data file does not have a publication date then the file system last modified date/time can be used instead." An in-memory data file with no date still sends nothing.Engine 'No Engine'. The builder now gives the data file its engine's type name, taken from the builder's own name (DeviceDetectionHashEngineBuildergivesDeviceDetectionHashEngine), and the update service logs it until the engine is set.AspectEngineDataFileDefaultgainsgetEngineTypeNameandsetEngineTypeName.PipelineConfigurationExceptionwhen auto update or update on start-up is on, the update URL is the Distributor's and no licence key is set. The message namessetAutoUpdate(false)orsetDataUpdateOnStartup(false), whichever is on. Defaults: the licence key is "required before the Engine builds with autoUpdate or updateOnStartup on ... The build fails with a configuration error that names the option to turn off." A key is not required when the URL is not the Distributor's, which is how an update source that serves the file without one is configured.OnPremiseAspectEngineBuilderBasegains a protectedisLicenseKeyRequiredthat returns false, andFiftyOneOnPremiseAspectEngineBuilderBasereturns true for the Distributor's URL. Every data file is checked before any is registered, so a builder with several data files never registers the first, or runs its update on start-up, before refusing the build for a later one.downloadFilenow names the URL through oneloggedUrl, made withLicenseKeyMask.maskInfrom #85, and the error detail and the exception logged with a failed connection or download are masked withLicenseKeyMask.maskInas well. This is the part of #10 that this draft's new messages reopened. ThegetIdForLogginghelper, which only the replaced messages used, is removed.AUTO_UPDATE_ERR_READING_STREAM, whose documentation is "Used when IO operations with input or output stream failed". A failed read is still the download warning.checkForUpdatecompared the data file with the engine's temporary file only when the file system watcher flag was off. The watcher now exists only when auto update is on, so with auto update off and the flag at its default of true, a programmatic update never looked at the file on disk. Programmatic update: "If automatic update via HTTP is not enabled and a file data source is specified then it causes immediate refresh of the Engine using the file at the configured location, irrespective of whether Automatic Update from File is enabled." A programmatic update now always compares the files, and the timer compares them whenever no watcher exists, where it used to go by the flag.PipelineConfigurationExceptionthe builder throws when auto update or update on start-up is on and no data update service was given, inOnPremiseAspectEngineBuilderBase.javalines 229 and 232 to 233. "an DataUpdateService" becomes "a DataUpdateService" in both places and "to the the pipeline builder" loses the doubled word. No test asserts that text, and a caller who reads the message sees the corrected wording.updateFromMemoryand the download thatcheckForUpdateFromUrlkeeps in memory when there is no temporary file, because both pass the data to the engine as bytes withrefreshData(identifier, byte[])and try once, rather than pointing it at a file and trying 10 times. Both are at error level and carry the exception, as .NET and Go word them. Before, Java logged "An error occurred while applying a data file update to <type>. Error detail: <message>".Behaviour that changes for callers
setAutoUpdate(false), as the specification's defaults say. The tests that built with the defaults now do this or give a key, and the examples already did. This belongs in the release note.setDataUpdateOnStartup(true)now works withsetAutoUpdate(false).LicenseKeys=********************3LPA.checkForUpdatereturnsAUTO_UPDATE_ERR_READING_STREAMfor it, where it was the download warning andAUTO_UPDATE_HTTPS_ERR.checkForUpdatecalled by the application refreshes the engine from the file on disk when that file is newer, whatever the watcher flag says.isLicenseKeyRequired.Other open drafts
#85 is merged into
rc, and its change todownloadFileconflicted with this draft's. The first merge ofrc(06e3a21) keeps this draft's messages, and the next commits mask them. The second merge ofrc(76eb957,rcat ff6e4c0, which holds #92) had no text conflict, but #92 addedDataUpdateService_CheckForUpdate_FileRefreshFails_TimerSetAgain, which allowed for a warning on each of the 10 refresh tries. This draft logs each try at debug and one error after the last, the Errors table's "Error when calling RefreshData on Engine" row, so 0c7e639 makes the test allow that one error.git merge-treeof this head against every other open draft's head finds conflicts only with #97, #67, #63 and #43, and each of those conflicts withrcitself.The third merge of
rc(3a3cfe4,rcat 1c2c578) brings in #98, which bounds the wait for a watched data file at ten minutes and registers the file again after a watched refresh, and moves thecxxsubmodule to c6d4e4d9. Git mergedDataUpdateServiceDefault.javawith no conflict, because #98 changes only the watched file branch ofcheckForUpdate, which this draft does not touch, and its newDataUpdateServiceWatchedFileTestssit beside this draft's tests. Commit 031a329 mergesrcagain at ce406b2, which holds #80, #99 and #88, and the head 546696d mergesrcat 528e0df, which adds #59, #61, #62, #101 and #108. Neither touches any of this draft's files.How it was checked
Tests were written first and fail on
rc.DataUpdateServiceLoggingTestsis new. It reads each message and level from the specification and checksIf-Modified-Sincefor a data file with and without a published date. Six of its tests give the data file a real-looking licence key in its URL and check, at every level, that each message and each exception logged with one holds only the masked key, for a 304, a 200, a failed connection, a failed download, a 403 and no response. One checks the write error text.DataUpdateServiceTestsnow expects no file system watcher when auto update is off, a warning, not an error, for a failed connection or a 429, and a programmatic update that refreshes from a newer file on disk with auto update off and the watcher flag at its default.FiftyOneDegreesOnPremiseAspectEngineBuilderBaseTestschecks registration with update on start-up alone, and the licence key refusal and its exceptions.OnPremiseAspectEngineBuilderBaseTestchecks that a builder with two data files, the second without a key, refuses the build before it registers either. A single file builder refuses two data files before it reaches this check, so the test uses the base builder.UpdateOnStartup_AutoUpdateOff_StartsNoTimerinDataUpdateServiceLoggingTestspasses onrcas well, so it guards the behaviour and does not prove the change. D3 is proved byUpdateOnStartup_AutoUpdateOff_DataFileRegisteredinFiftyOneDegreesOnPremiseAspectEngineBuilderBaseTestsand by the auto update off cases ofDataUpdateService_Register_FileSystemWatcherinDataUpdateServiceTests, which both fail in the first red run below.rcwithoutsetEngineTypeNamerc, without the fixes. On Ubuntu and Windows, 7 of 25 logging tests (the six licence key tests and the write error), all 7DataUpdateServiceLicenseKeyTestsfrom #85, the 4 cases of the file comparison test andLaterDataFileHasNoLicenseKey_NothingRegisteredfailDataUpdateService_CheckForUpdate_FileRefreshFails_TimerSetAgain("1 errors occurred during test", the refresh error), and the other jobs were cancelled. Fixed in 0c7e639Setting 'PullRequestSha'line, in Configure and in each of the 9 jobs, is 0c7e639, the head, and Configure printed "Left out 6 macOS entries"| Red, the refresh failure message | run 43034146958 | The tests (5365b94) without the fix. On Java 8 and 21, the 4 rows of
DataUpdateService_CheckForUpdate_FileRefreshFails_TimerSetAgainof 132DataUpdateServiceTestsandUpdateFromMemory_RefreshFails_LogsTheTableMessageof 25DataUpdateServiceLoggingTestsfail || Green, the refresh failure message | run 43034177333 | d8f75d7. On Java 8 and 21, all 132 and all 25 pass |
| Full pull request build | run 43071987865 | Head 546696d, which merges
rcat 528e0df. All 9 jobs without macOS passed, being Ubuntu Java 8, 11, 17 and 21, Ubuntu ARM Java 17, and Windows Java 8, 11, 17 and 21. EverySetting 'PullRequestSha'line, in Configure and in each of the 9 jobs, is 546696d, and Configure printed "Left out 6 macOS entries" || Earlier attempt on 031a329 | run 43051980968 | Failed at "Pipeline developer examples" for want of a resource key, which #108 fixed in
rc|| Earlier full build | run 43015715793 | Head 3a3cfe4, before the refresh failure message. Every job passed |
| Earlier full build | run | Head 10a8a5d, before the merge of
rcthat brings in #98. All 9 jobs without macOS passed, being Ubuntu Java 8, 11, 17 and 21, Ubuntu ARM Java 17, and Windows Java 8, 11, 17 and 21. EverySetting 'PullRequestSha'line, in Configure and in each of the 9 jobs, is 10a8a5d, the head, and Configure printed "Left out 6 macOS entries" |macOS was left out of this draft's full build under the 9 November decision. The rc head is built with macOS before it merges into main.
Every exit path of
downloadFile,downloadOkResponse,checkForUpdate,checkForUpdateFromUrl,updatedFileAvailable,updateFromMemoryandpreCreateEnginewas read after the change. The new temporary file path check returnsAUTO_UPDATE_REFRESH_FAILEDwhere a missing path used to throw aNullPointerExceptionout of the timer thread.What remains
pipeline/Gaps-vs-spec.mdfile is not edited here. Its removal is for whoever closes the list.