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

Azurite returns duplicate odata.etag in table rows for table cloned with Azure Storage Explorer #1536

Closed
thomaseyde opened this issue Jun 10, 2022 · 4 comments · Fixed by #2315
Assignees
Labels
table-storage Relating to Azurite table storage implementation

Comments

@thomaseyde
Copy link

thomaseyde commented Jun 10, 2022

Which service(blob, file, queue, table) does this issue concern?

table

Which version of the Azurite was used?

3.17.1

Where do you get Azurite? (npm, DockerHub, NuGet, Visual Studio Code Extension)

npm via Rider Azure plugin

What's the Node.js version?

12.18.3

What problem was encountered?

Duplicate property odata.etag found in serialized json response after querying on RowKey. This leads to a System.Private.CoreLib: Exception while executing function: (...). System.Private.CoreLib: An item with the same key has already been added. Key: odata.etag. in Azure.Data.Tables v12.5.0.

Partial stack trace:

Function '... (Activity)' failed with an error. Reason: System.ArgumentException: An item with the same key has already been added. Key: odata.etag
   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
   at Azure.Data.Tables.Models.TableEntityQueryResponse.DeserializeTableEntityQueryResponse(JsonElement element)
   at Azure.Data.Tables.TableRestClient.QueryEntitiesAsync(String table, Nullable`1 timeout, String nextPartitionKey, String nextRowKey, QueryOptions queryOptions, CancellationToken cancellationToken)
   at Azure.Data.Tables.TableClient.<>c__DisplayClass49_0`1.<<QueryAsync>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at Azure.Core.PageableHelpers.FuncAsyncPageable`1.AsPages(String continuationToken, Nullable`1 pageSizeHint)+MoveNext()
   at Azure.Core.PageableHelpers.FuncAsyncPageable`1.AsPages(String continuationToken, Nullable`1 pageSizeHint)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult()
   at Azure.AsyncPageable`1.GetAsyncEnumerator(CancellationToken cancellationToken)+MoveNext()
   at Azure.AsyncPageable`1.GetAsyncEnumerator(CancellationToken cancellationToken)+MoveNext()
   at Azure.AsyncPageable`1.GetAsyncEnumerator(CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult()

Steps to reproduce the issue?

  1. Start Azurite
  2. Clone a table with Microsoft Azure Storage Explorer v1.23.1
  3. Query the cloned table in code (C#) using Azure.Data.Tables v12.5.0

Extract from Azurite debug log, trimmed:

2022-06-10T06:36:18.665Z 7653d0fa-125c-49f1-ace1-4d0d93cac7e4 info: TableStorageContextMiddleware: RequestMethod=GET RequestURL=http://127.0.0.1/devstoreaccount1/Office365UserDetails()?$format=application%2Fjson%3Bodata%3Dminimalmetadata&$filter=%28%28PartitionKey%20eq%20%27acme%27%29%20and%20%28RowKey%20ge%20%272022-02-13%27%29%29%20and%20%28RowKey%20lt%20%272022-02-14%27%29 RequestHeaders:{"host":"127.0.0.1:10002","x-ms-version":"2019-02-02","dataserviceversion":"3.0","accept":"application/json; odata=minimalmetadata","x-ms-client-request-id":"4a9abf9d-2b33-4929-a11b-545089905dfb","x-ms-return-client-request-id":"true","user-agent":"azsdk-net-Data.Tables/12.5.0 (.NET 6.0.4; Microsoft Windows 10.0.19044)","x-ms-date":"Fri, 10 Jun 2022 06:36:18 GMT","authorization":"SharedKeyLite devstoreaccount1:cwl6q0C7cMOwgwWJoYRcdtf3q88w3xILIOD48rtGNhI="} ClientIP=127.0.0.1 Protocol=http HTTPVersion=1.1
2022-06-10T06:36:18.665Z 7653d0fa-125c-49f1-ace1-4d0d93cac7e4 debug: tableStorageContextMiddleware: Dispatch pattern string: /Office365UserDetails()
2022-06-10T06:36:18.665Z 7653d0fa-125c-49f1-ace1-4d0d93cac7e4 info: tableStorageContextMiddleware: Account=devstoreaccount1 tableName=Office365UserDetails
2022-06-10T06:36:18.665Z 7653d0fa-125c-49f1-ace1-4d0d93cac7e4 verbose: DispatchMiddleware: Dispatching request...
2022-06-10T06:36:18.666Z 7653d0fa-125c-49f1-ace1-4d0d93cac7e4 info: DispatchMiddleware: Operation=Table_QueryEntities
2022-06-10T06:36:18.666Z 7653d0fa-125c-49f1-ace1-4d0d93cac7e4 verbose: AuthenticationMiddlewareFactory:createAuthenticationMiddleware() Validating authentications.
2022-06-10T06:36:18.666Z 7653d0fa-125c-49f1-ace1-4d0d93cac7e4 info: TableSharedKeyLiteAuthenticator:validate() Start validation against account shared key authentication.
2022-06-10T06:36:18.666Z 7653d0fa-125c-49f1-ace1-4d0d93cac7e4 info: TableSharedKeyLiteAuthenticator:validate() [STRING TO SIGN]:"Fri, 10 Jun 2022 06:36:18 GMT\n/devstoreaccount1/devstoreaccount1/Office365UserDetails()"
2022-06-10T06:36:18.666Z 7653d0fa-125c-49f1-ace1-4d0d93cac7e4 info: TableSharedKeyLiteAuthenticator:validate() Calculated authentication header based on key1: SharedKeyLite devstoreaccount1:cwl6q0C7cMOwgwWJoYRcdtf3q88w3xILIOD48rtGNhI=
2022-06-10T06:36:18.666Z 7653d0fa-125c-49f1-ace1-4d0d93cac7e4 info: TableSharedKeyLiteAuthenticator:validate() Signature 1 matched.
2022-06-10T06:36:18.666Z 7653d0fa-125c-49f1-ace1-4d0d93cac7e4 verbose: DeserializerMiddleware: Start deserializing...
2022-06-10T06:36:18.666Z 7653d0fa-125c-49f1-ace1-4d0d93cac7e4 info: HandlerMiddleware: DeserializedParameters={"options":{"queryOptions":{"format":"application/json;odata=minimalmetadata","filter":"((PartitionKey eq 'acme') and (RowKey ge '2022-02-13')) and (RowKey lt '2022-02-14')"},"requestId":"4a9abf9d-2b33-4929-a11b-545089905dfb","dataServiceVersion":"3.0"},"version":"2019-02-02"}
2022-06-10T06:36:18.684Z 7653d0fa-125c-49f1-ace1-4d0d93cac7e4 debug: TableHandler:queryEntities() Raw response string is "{\"odata.metadata\":\"http://127.0.0.1:10002/devstoreaccount1/$metadata#Tables/@Element\",\"value\":[{\"odata.etag\":\"W/\\\"datetime'2022-06-10T06%3A21%3A42.279353Z'\\\"\",\"PartitionKey\":\"acme\",\"RowKey\":\"2022-02-13:user@acme.com\",\"odata.etag\":\"W/\\\"datetime'2022-06-09T10%3A28%3A59.435654Z'\\\"\",\"ReportDate\":\"2022-02-13\",\"CustomerId\":\"acme\",\"ReportRefreshDate\":\"2022-02-13\",\"UserPrincipalName\":\"user@acme.com\"}]}"
2022-06-10T06:36:18.684Z 7653d0fa-125c-49f1-ace1-4d0d93cac7e4 verbose: SerializerMiddleware: Start serializing...
2022-06-10T06:36:18.684Z 7653d0fa-125c-49f1-ace1-4d0d93cac7e4 info: Serializer: Start returning stream body.
2022-06-10T06:36:18.687Z 7653d0fa-125c-49f1-ace1-4d0d93cac7e4 info: EndMiddleware: End response. TotalTimeInMS=22 StatusCode=200 StatusMessage=OK Headers={"server":"Azurite-Table/3.17.1","content-type":"application/json;odata=minimalmetadata","x-ms-client-request-id":"4a9abf9d-2b33-4929-a11b-545089905dfb","x-ms-request-id":"7653d0fa-125c-49f1-ace1-4d0d93cac7e4","x-ms-version":"2021-06-08","date":"Fri, 10 Jun 2022 06:36:18 GMT"}

The relevant details are in the raw response string, formatted. Property odata.etag found twice:

[
  {
    "odata.etag": "W/\"datetime'2022-06-10T06%3A21%3A42.279353Z'\"",
    "PartitionKey": "acme",
    "RowKey": "2022-02-13:user@acme.com",
    "odata.etag": "W/\"datetime'2022-06-09T10%3A28%3A59.435654Z'\"",
    "ReportDate": "2022-02-13",
    "CustomerId": "acme",
    "ReportRefreshDate": "2022-02-13",
    "UserPrincipalName": "user@acme.com"
  }
]

Have you found a mitigation/solution?

No

@edwin-huber edwin-huber added the table-storage Relating to Azurite table storage implementation label Jun 22, 2022
@boggylp
Copy link

boggylp commented Jul 25, 2022

Workaround is to touch/update the copied rows in local storage because it "resets" the etag value to a non duplicate. But this works only via storage explorer.

@Zhaph
Copy link

Zhaph commented Jan 13, 2023

Also happens if you "copy table" from one account and then "paste table" into the Azurite hosted instance.

@gluwer
Copy link

gluwer commented Jan 18, 2023

I have encountered similar issue with new @azure/data-tables library for node. This new library returns etag as a part of entity like any other property but then by default it is also send in updates. Old emulator or production service just ignores etag property, but Azurite seems to save it. In subsequent updates you start to get 409 if you set etags to match.
To get it working properly you have to remove etag from entity but it is not required on production servers.

This is really the same issue, as i think Azure Storage Explorer uses new @azure/data-tables nodejs library under the hood.

@runebrg
Copy link

runebrg commented Oct 2, 2023

Work around that worked for me: Use export to file and then import into the new table instead of using clone or copy/paste in Storage Explorer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
table-storage Relating to Azurite table storage implementation
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants