allthingssecurity opened a new pull request, #28073: URL: https://github.com/apache/camel/pull/28073
# Description [CAMEL-25758](https://issues.apache.org/jira/browse/CAMEL-25758) With `moveAfterRead=true` the blob consumer moves a consumed blob as copy + delete: `beginCopy(...).waitForCompletion(timeout)`, then `deleteBlob`. The result of `waitForCompletion` was not looked at. A Copy Blob is asynchronous on the service: it is `pending` until it ends as `success`, `aborted` or `failed`. The `SyncPoller` of `BlobClientBase.beginCopy` does not throw when the copy does not succeed. It returns the last poll response once its status is complete: `FAILED` for a failed copy, `USER_CANCELLED` for an aborted copy, and the custom complete status `POLLING_FAILED` when reading the status of a pending copy failed. Apart from a copy request that the service refuses (`BlobStorageException` when the copy starts), only a timeout throws. In these three cases the consumer deleted the source blob anyway. The destination of a failed or aborted copy has no content, and a copy that is still pending when its source is deleted fails ([Copy Blob remarks](https://learn.microsoft.com/en-us/rest/api/storageservices/copy-blob#remarks)). The blob was lost from both containers, and Camel logged nothing. The move is always within one storage account, where a copy "can complete synchronously" (Microsoft Learn). A copy that completes synchronously is not affected; the loss needs a copy that the service runs asynchronously and that then fails or is aborted, or a failed status read while it is pending. This change deletes the source blob only when the copy ended with `SUCCESSFULLY_COMPLETED`. Otherwise the copy status goes to the consumer's exception handler and the blob stays in the source container, so the next poll consumes it again. That is what already happens when the copy request itself fails with a `BlobStorageException`. `BlobConsumer` is the only production change. Not changed: - a copy that times out still throws out of the on completion (logged by Camel) and keeps the source blob, as before; - `deleteAfterRead` without `moveAfterRead` is unchanged; - a blob whose copy keeps failing is consumed and copied again at every poll, with one exception handler call each time, as a blob whose copy request is refused is today. Trade-off: `POLLING_FAILED` does not say whether the copy succeeded. A copy that did succeed is now copied and routed again on the next poll (at least once instead of a possible loss). If the status can never be read (credentials that may write but not read the destination container), every move is reported and retried, where main deleted the source without knowing the outcome. Tests: - New `BlobConsumerMoveAfterReadCopyStatusTest` (4 tests, Mockito clients as in `BlobConsumerInProgressTest`, the poller returns the given status): the source blob is kept and the exception handler is called when the copy ends `FAILED`, `USER_CANCELLED` or `POLLING_FAILED`. A fourth test checks that the source blob is deleted after a successful copy. - Without the change (main's `BlobConsumer`, same tests) the three "kept" tests fail with `NeverWantedButInvoked: blobClient.delete()`. The fourth passes on main too. It is there because the fix could break the success path. - With the change, the camel-azure-storage-blob unit tests pass: 68 run, 0 failures. The integration tests need Azure (or Azurite) and were not run. How it was found: deterministic simulation testing with a fake Azure storage world. The seed picked `moveAfterRead` or `deleteAfterRead`, paging, poll delay, timeout, faults, and how often a copy stays pending and then fails, is aborted or cannot be read. The invariant "every blob is still in the source container or was moved with its content" failed on main only with `moveAfterRead=true` and a copy that did not succeed. With this change those seeds pass. The simulation harness is not part of this PR. # Target - [x] I checked that the commit is targeting the correct branch (Camel 4 uses the `main` branch) # Tracking - [x] If this is a large change, bug fix, or code improvement, I checked there is a [JIRA issue](https://issues.apache.org/jira/browse/CAMEL) filed for the change (usually before you start working on it). # Apache Camel coding standards and style - [x] I checked that each commit in the pull request has a meaningful subject line and body. - [ ] I have run `mvn clean install -DskipTests` locally from root folder and I have committed all auto-generated changes. (I built and tested camel-azure-storage-blob, including the formatter and import-sort plugins. I did not run the full root build.) # AI-assisted contributions - [x] If this PR includes AI-generated code, commits have proper co-authorship attribution (e.g., `Co-authored-by` trailers) and the PR description identifies the AI tool used. This PR was prepared with Claude Code (Claude Opus 5.5). The commit carries a `Co-Authored-By` trailer. _Claude Code on behalf of allthingssecurity_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
