Skip to content

Give the result of an update from memory to its caller alone - #169

Merged
jwrosewell merged 2 commits into
rcfrom
fix/update-from-memory-result
Nov 15, 2026
Merged

jwrosewell merged 2 commits into
rcfrom
fix/update-from-memory-result

Conversation

@jwrosewell

@jwrosewell jwrosewell commented Nov 15, 2026 •

Copy link
Copy Markdown
Contributor

An update from memory gave its result to the update complete callbacks as well as to its caller, returned a failure as a status, and logged it. It now reports nothing to the callbacks, returns AUTO_UPDATE_SUCCESS, and raises a failure to its caller without logging it, as the data update specification says.

Closes #149.

Problem

DataFileUpdateService.update_from_memory (datafile_update_service.py lines 310 to 363 at 28e5bf2c) passed its status to the update complete callbacks on both of its paths (self.__emit(status, data_file)), returned AUTO_UPDATE_REFRESH_FAILED or the status of a failed write rather than raising, and logged the failure through _refresh and _write_data_file (#149).

What the specification says

data-updates.md lines 372 to 376 at 706de1f0:

Two calls report nothing to onUpdateCallbacks, and give their result to the caller instead. A programmatic check for a data file the Engine has no configuration for returns the no configuration status (AUTO_UPDATE_NO_CONFIGURATION), and an update from memory returns success or fails with an error.

Lines 477 to 479: "A programmatic check, the update on start-up and an update from memory pass the error to their caller instead of logging it."

What changed

All in pipeline/fiftyone_pipeline_engines.

  1. cda98342, the tests, in tests/test_datafile_update_http.py. New: testAFailedRefreshRaisesAndIsNeitherReportedNorLogged (a data file kept in memory keeps its old data), testAFailedRefreshOfAFileDataFileRaisesAndIsNeitherReportedNorLogged, testAFailedTemporaryCopyFromMemoryRaisesAsAnyOtherError, testFailedCopyFromMemoryRaisesAnErrorNamingThePath, testNoTemporaryPathFromMemoryRaises and testAnUpdateFromMemoryCallsNoCallback. Changed, as each asserted the old behaviour: testMemoryDataFileIsRefreshedFromTheBytes and testFileDataFileIsWrittenThenRefreshed now expect no callback and only the "Attempting to refresh" log entry, testAFailedWriteFromMemoryEndsTheUpdate expects an error with nothing reported or logged, the earlier testFailedRefreshIsReportedAndLogged and testACallbackErrorOnAnUpdateFromMemoryIsLoggedAndStopsTheCallbacks are replaced by the tests above, and testFailedCopyNamesThePath and testNoTemporaryPathIsReported now run through update_data_file, so the logging and reporting of the check route stay tested.
  2. 502cadcb, the change. A new public DataUpdateException(message, status) carries the failure's status, with the error that caused it chained. update_from_memory reports nothing to the callbacks, returns AUTO_UPDATE_SUCCESS once the engine is refreshed, and raises DataUpdateException for a failed refresh, with "An error occurred when applying a data update to engine ''." and AUTO_UPDATE_REFRESH_FAILED, and for a failed write of a data file kept on disk, with that write's message and status. Neither is logged. The engine keeps the data it had. The update lock and the updating flag work as before. On the check route a failed write is logged and reported exactly as before, through _log_failed_write.

Before you upgrade

  • update_from_memory no longer calls the update complete callbacks.
  • A failed refresh or a failed write raises DataUpdateException, whose status is the status it returned before (AUTO_UPDATE_REFRESH_FAILED, AUTO_UPDATE_NEW_FILE_CANT_RENAME, AUTO_UPDATE_TEMP_PATH_NOT_SET or AUTO_UPDATE_UNKNOWN_ERROR), and it is not logged. Code that checked the returned status for a failure catches the exception instead.
  • For a data file whose engine has been closed it still returns AUTO_UPDATE_NOT_NEEDED and does nothing.

How it was verified

  • Red, run 43663363084 on 0a4a927, being cda9834 (the tests on rc 1190974) plus only a throwaway workflow. On Ubuntu and Windows the nine new and changed update from memory tests failed, "9 failed, 43 passed" in test_datafile_update_http.py and "9 failed, 85 passed" across the engines tests, and the two check route tests passed.
  • Green, run 43663371813 on be7cc95, being the head 502cadc plus the same workflow, "52 passed" and "94 passed" on both.
  • Full build 43664180038 on the head 502cadc, from rc 1190974, is green. Its ten Build and Test jobs, Python 3.10 to 3.14 on Ubuntu and Windows, each passed, with test_datafile_update_http.py passing in each. The 11 Setting 'PullRequestSha' lines all name 502cadc, and Configure printed "Left out 5 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.

What it releases

fiftyone_pipeline_engines and its dependants, which Monday's release plan releases already.

What remains

  1. The errors table's row for a failed write of data passed in from memory (line 511) says it is a warning and the engine is still refreshed. Here a failed write ends the update with an error, as the engines of data files kept on disk refresh by reading the file again, and refreshing them from the bytes given would need the data file to switch to memory, which is a larger change, tracked in #171.
  2. After an update from memory writes a data file kept on disk, the file system watcher's next pass sees the new file and refreshes the engine again, reporting that to the callbacks, because the time of the written file is not recorded, tracked in #170.
  3. Whether "returns success or fails with an error" also binds a call on a data file whose engine has been closed is left as it was, and asked in specifications#164.

The data updates specification says an update from memory reports nothing
to the update complete callbacks, and either returns success or fails with
an error that is passed to the caller rather than logged. That is
data-updates.md lines 372 to 376 at 706de1f0, with lines 312 to 316 for a
buffer passed in by a programmatic update from memory and lines 477 to 479
for errors. The errors table's row for data held in memory, line 510, gives
the message "An error occurred when applying a data update to engine
'<Engine type name>'." and the status AUTO_UPDATE_REFRESH_FAILED.

These tests fail on the current code, which reports each update from memory
to the callbacks, returns a failure status rather than raising, and logs the
failure.

New tests in test_datafile_update_http.py:

- testAFailedRefreshRaisesAndIsNeitherReportedNorLogged, for a data file
  kept in memory, whose data goes back to what it was
- testAFailedRefreshOfAFileDataFileRaisesAndIsNeitherReportedNorLogged, for
  a data file kept on disk
- testAFailedTemporaryCopyFromMemoryRaisesAsAnyOtherError
- testFailedCopyFromMemoryRaisesAnErrorNamingThePath
- testNoTemporaryPathFromMemoryRaises

Each failure is a DataUpdateException whose status is the failure's status,
whose message is the message of its row of the errors table, and whose cause
is the error that caused it. The data file is no longer marked as updating
afterwards. The only entry logged is the attempt to refresh the engine,
which is a message during operation and not an error.

Existing tests changed, because each asserted the behavior the
specification rules out:

- testMemoryDataFileIsRefreshedFromTheBytes and
  testFileDataFileIsWrittenThenRefreshed asserted that a successful update
  from memory reported AUTO_UPDATE_SUCCESS to the callbacks. They now assert
  that nothing is reported and that only the attempt is logged.
- testFailedRefreshIsReportedAndLogged asserted a returned
  AUTO_UPDATE_REFRESH_FAILED and a logged error. It is replaced by
  testAFailedRefreshRaisesAndIsNeitherReportedNorLogged.
- testAFailedWriteFromMemoryEndsTheUpdate asserted a returned
  AUTO_UPDATE_NEW_FILE_CANT_RENAME that was also reported to the callbacks
  and logged. It now asserts the error raised, with nothing reported or
  logged. The update still ends without refreshing the engine.
- testFailedCopyNamesThePath and testNoTemporaryPathIsReported asserted the
  logged message and the returned status through update_from_memory. They
  now make the same assertions through a check for an update, which still
  logs and reports, so that route stays covered, and the new tests above
  cover update_from_memory.
- testACallbackErrorOnAnUpdateFromMemoryIsLoggedAndStopsTheCallbacks
  asserted that update_from_memory called the callbacks. It is replaced by
  testAnUpdateFromMemoryCallsNoCallback.

Refs #149
update_from_memory reported its status to the update complete callbacks,
returned a failure status, and logged the failure. The data updates
specification says an update from memory reports nothing to
onUpdateCallbacks and returns success or fails with an error, in
data-updates.md lines 372 to 376 at 706de1f0, and that the error goes to the
caller instead of being logged, in lines 312 to 316 and lines 477 to 479.

update_from_memory now behaves as follows:

- It reports nothing to the update complete callbacks on any path.
- It returns AUTO_UPDATE_SUCCESS when the engine was refreshed.
- It raises DataUpdateException when the refresh fails, with the message of
  the errors table's row for data held in memory, line 510, which is "An
  error occurred when applying a data update to engine '<Engine type
  name>'.", the status AUTO_UPDATE_REFRESH_FAILED, and the refresh error
  chained as its cause. Nothing is logged apart from the attempt to refresh
  the engine. The engine keeps the data it had, and a data file kept in
  memory gets its previous data back, as before.
- It raises DataUpdateException when a data file kept on disk cannot be
  written, with the message and status the write failure already had, the
  error chained as its cause, and nothing logged. The engine is not
  refreshed, as before.

A failed refresh of a data file kept on disk from update_from_memory now
carries the row for data held in memory rather than the row for a refresh
from the data file, because the data came from the caller. .NET's
UpdateFromMemory does the same.

DataUpdateException is a new class in datafile_update_service, named as
.NET's is, with a status attribute. No public name is removed or renamed.
The update lock and the updating flag are held and restored as before.

The write helper now raises rather than logging, and the check for an
update logs the failure itself, with the same message, level and status,
and reports it to the callbacks as before. Update polling, the file system
watcher and the programmatic check are otherwise unchanged.

A data file that has been unregistered, because its engine was closed,
still returns AUTO_UPDATE_NOT_NEEDED with nothing done. The specification
gives an update from memory two results, success or an error, and does not
describe an update from memory after the engine has been closed. The no
configuration status in lines 338 to 339 and 372 to 375 is for a
programmatic check, not for an update from memory. So the specification
does not settle this case, and it is left as it was.

The errors table's row for a failed write of data passed in from memory,
line 511, is still not followed. That row is a warning, after which the
engine is still refreshed. The engine of a data file kept on disk here
refreshes by reading the file, so it cannot take data that was not written,
and the update ends with an error instead.

Refs #149
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant