Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Azure.Data.Tables throws an exception and failes TableTransactionAction when object to delete does not exist #30462

Closed
edwinvdb opened this issue Aug 11, 2022 · 6 comments
Assignees
Labels
Client This issue points to a problem in the data-plane of the library. customer-reported Issues that are reported by GitHub users external to the Azure organization. needs-team-attention Workflow: This issue needs attention from Azure service team or SDK team question The issue doesn't require a change to the product in order to be resolved. Most issues start as that Tables

Comments

@edwinvdb
Copy link

edwinvdb commented Aug 11, 2022

Library name and version

Azure.Data.Tables 12.6.1

Describe the bug

Lets start with TableClient.DeleteEntity. If you call this with a partitionKey and rowKey that does not exists (also for Etag.All or without Etag), it returns a Response with a 404, while the documentation says:

If the entity exists, the Response indicating the result of the operation. If the entity does not exist, null

However, it does not throw an Exception.

If you use a TableTransactionAction with TableTransactionActionType.Delete and one of the entries does not exist, a RequestFailedException exception will be thrown and the batch will be rollbacked, probably because an individual entry was not existent and thus resulted in a 404 .

Expected behavior

  • For TableClient.DeleteEntity to match te documentation to return null when the item did not exist
  • For TableClient.SubmitTransaction with a TableTransactionAction that has some TableTransactionActionType.Delete to succeed even if the to be delete items did not exist

Actual behavior

  • TableClient.DeleteEntity returns a Response with a 404 instead of null when the item did not exist.
  • TableClient.SubmitTransaction with a TableTransactionAction that has some TableTransactionActionType.Delete throws a RequestFailedException when some of the items to be deleted do not exist.

Reproduction Steps

Response exists = _TableClient.DeleteEntity("partitionKey", "rowkey", ETag.All);
Assert.IsTrue(null, exist);

Response exists = _TableClient.DeleteEntity("partitionKey", "rowkey");
Assert.IsTrue(null, exist);

List<TableTransactionAction> deletes = new List<TableTransactionAction>();
deletes.Add(new TableTransactionAction(TableTransactionActionType.Delete,
                        new TableEntity { PartitionKey ="partitionkey", RowKey = "rowkey") }));

_TableClient.SubmitTransaction(deletes)

Environment

Azure WebApp

@ghost ghost added needs-triage Workflow: This is a new issue that needs to be triaged to the appropriate team. customer-reported Issues that are reported by GitHub users external to the Azure organization. question The issue doesn't require a change to the product in order to be resolved. Most issues start as that labels Aug 11, 2022
@azure-sdk azure-sdk added Client This issue points to a problem in the data-plane of the library. needs-team-triage Workflow: This issue needs the team to triage. Tables labels Aug 11, 2022
@ghost ghost removed the needs-triage Workflow: This is a new issue that needs to be triaged to the appropriate team. label Aug 11, 2022
@m-redding m-redding added needs-team-attention Workflow: This issue needs attention from Azure service team or SDK team and removed needs-team-triage Workflow: This issue needs the team to triage. labels Aug 11, 2022
@m-redding
Copy link
Member

Thank you for your feedback. Tagging and routing to the team member best able to assist.

@christothes
Copy link
Member

Hi @edwinvdb Sorry you encountered this problem.

The documentation bug is a known issue being tracked by #25561 - This also relates to a larger issue tracked by #25626 which we should be able to address in the near term which allows configuration of acceptable error responses that should not throw.

Regarding the actual behavior, throwing the 404 is currently intended for the Delete operation when the item does not exist, and this behavior is consistent with the guideline that all failure cases will throw by default.

In the case of batch operations, this behavior is controlled by the service, and cannot be changed by the client. This is the intended behavior of transactional batch operations where all operations succeed "all or nothing". The documentation can be found here

@edwinvdb
Copy link
Author

Hi @christothes , thanks for your quick response.

You say:
"Regarding the actual behavior, throwing the 404 is currently intended for the Delete operation when the item does not exist, and this behavior is consistent with the guideline that all failure cases will throw by default."

But this isn't true, the DeleteEntity won't throw, but it will just return a response with a 404 when there was nothing to delete. This seems quite reasonable to me: when you ask to delete something, you don't want it to exist and as that was already the case, from a functional perspective there is no error. This also aligns with normal SQL database behavior for a DELETE statement.

For insert/updates we have an Upsert operation, maybe there could also be a "DeleteIfExists" operation?

You write "This is the intended behavior of transactional batch operations where all operations succeed "all or nothing""
I agree with the "all or nothing" part, but the DeleteEntity operation succeeds code-flow wise, even if the object to delete did not exist, as no exception is thrown and you just get a statusresponse back with a 404. This is different from CreateEntity, which does throw when the entity already exists.

So it seems to me there is a discrepancy here between a DeleteEntity that kind of succeeds without an exception and a batch of Deletes that don't succeed and throw an exception. Deleting an entity only throws within the context of a batch operation and that seems inconsistent.

What is your view on this?

@christothes
Copy link
Member

Hi @christothes , thanks for your quick response.

You say: "Regarding the actual behavior, throwing the 404 is currently intended for the Delete operation when the item does not exist, and this behavior is consistent with the guideline that all failure cases will throw by default."

But this isn't true, the DeleteEntity won't throw, but it will just return a response with a 404 when there was nothing to delete. This seems quite reasonable to me: when you ask to delete something, you don't want it to exist and as that was already the case, from a functional perspective there is no error. This also aligns with normal SQL database behavior for a DELETE statement.

Ah, you are correct. Thanks for the correction here - I had confused the fact that we actually do internally throw, but just swallow the exception. We did want Delete to be treated as non-failing operations, since to try and reason about the state of the entity before the Delete is inherently a race condition.

For insert/updates we have an Upsert operation, maybe there could also be a "DeleteIfExists" operation?

You write "This is the intended behavior of transactional batch operations where all operations succeed "all or nothing"" I agree with the "all or nothing" part, but the DeleteEntity operation succeeds code-flow wise, even if the object to delete did not exist, as no exception is thrown and you just get a statusresponse back with a 404. This is different from CreateEntity, which does throw when the entity already exists.

So it seems to me there is a discrepancy here between a DeleteEntity that kind of succeeds without an exception and a batch of Deletes that don't succeed and throw an exception. Deleting an entity only throws within the context of a batch operation and that seems inconsistent.

What is your view on this?

Yes, the resulting behaviors are inconsistent, but, as mentioned above, we swallow the failure issued by the service for the Delete operation as an enhancement that we can control on the SDK side. For batch operations, we are limited by the service side behavior. I think the best we can do in this case is to improve the documentation to avoid unexpected behavior.

@edwinvdb
Copy link
Author

edwinvdb commented Aug 11, 2022

Yes, the resulting behaviors are inconsistent, but, as mentioned above, we swallow the failure issued by the service for the Delete operation as an enhancement that we can control on the SDK side. For batch operations, we are limited by the service side behavior. I think the best we can do in this case is to improve the documentation to avoid unexpected behavior.

Yes, I understand. Hopefully the service will improve to support "DeleteIfExists" batch operations ;-)

I'll leave closing the issue up to you, as I don't know if this issue should remain for the documentation improvement.

(As a final remark: I actually don't need the atomic batch behavior, but just use a batch to improve on the number of HTTP-calls...)

@christothes
Copy link
Member

I'll leave closing the issue up to you, as I don't know if this issue should remain for the documentation improvement.

I'll track this with #30494

@github-actions github-actions bot locked and limited conversation to collaborators Mar 24, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Client This issue points to a problem in the data-plane of the library. customer-reported Issues that are reported by GitHub users external to the Azure organization. needs-team-attention Workflow: This issue needs attention from Azure service team or SDK team question The issue doesn't require a change to the product in order to be resolved. Most issues start as that Tables
Projects
None yet
Development

No branches or pull requests

4 participants