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

'blank' etag is treated as "no etag" #2320

Closed
aronchick opened this issue Oct 23, 2020 · 4 comments · Fixed by #2666
Closed

'blank' etag is treated as "no etag" #2320

aronchick opened this issue Oct 23, 2020 · 4 comments · Fixed by #2666
Assignees

Comments

@aronchick
Copy link

/area runtime

What version of Dapr?

0.8

When using the following write to state store (redis), if I set the value of etag to be '' (blank), dapr treats this as having no etag.

g = daprClient.save_state(
                store_name=state_store,
                key=key_name,
                value=value,
                etag=etag,
                options=StateOptions(
                    consistency=Consistency.strong, concurrency=Concurrency.first_write
                ),
            )

This is not ideal. If this was in a thread where multiple threads read a value that did not exist, and then all tried to write it simultaneously, every one but the first write should fail from an etag mismatch.

RELEASE NOTE: FIX Bug in runtime.

@aronchick aronchick added the kind/bug Something isn't working label Oct 23, 2020
@aronchick
Copy link
Author

Ok, some additional details thanks to @youngbupark

Currently, Dapr treats inserts as "upserts" which create a the value if nothing exists (or no/blank etag is provided). So, in this case, where there is no etag on first request, later requests are treated as overwrites, which is not intuitive. Using pseudo-code:

first_etag = daprClient.get_state(state_store='stateStore', key='foo') # <= value does not exist, etag is ''
second_etag = daprClient.get_state(state_store='stateStore', key='foo') # <= value does not exist, etag is '' 

<... do some work ...>

daprClient.save_state(state_store='stateStore', key='foo', value='firstValue', etag=first_etag) # <= should work, there still is nothing in the database with this key
daprClient.save_state(state_store='stateStore', key='foo', value='firstValue', etag=second_etag) # <= should NOT work, key 'foo' now DOES have an etag, even though 'second_etag' is empty

@aronchick
Copy link
Author

To be clear, the user's expectation (I believe) would be around 'optimistic concurrency':

1. Optimistic concurrency – An application performing an update will as part of its update verify if the data has changed since the application last read that data. For example, if two users viewing a wiki page make an update to the same page then the wiki platform must ensure that the second update does not overwrite the first update – and that both users understand whether their update was successful or not. This strategy is most often used in web applications.

This could be addressed via metadata settings - e.g. concurrency_mode=ConcurrencyMode.optimistic

https://azure.microsoft.com/en-us/blog/managing-concurrency-in-microsoft-azure-storage-2/

@youngbupark youngbupark removed the kind/bug Something isn't working label Oct 27, 2020
@amolenk
Copy link

amolenk commented Jan 4, 2021

I agree there’s something missing in the way Dapr works with OCC right now. Not sure if it's a design issue or a bug, but it definitely should be looked at.

Let’s say there are multiple users working on a set of articles. Using Dapr first-write-wins and ETags we can be sure that no user overwrites the work of another user:

User 1 reads Article A (ETag: 3)
User 2 reads Article A (ETag: 3)
User 1 writes Article A (ETag: 3, ETag increased to 4 by server)
User 2 writes Article A (ETag: 3, ETag mismatch, update denied)

This works great.

However, if you start from a situation where there isn't any state saved yet and the users create new articles, Dapr’s OCC mechanism breaks down:

User 1 writes new Article A (ETag: “”, ETag increased to 1 by server)
User 2 writes new Article A (ETag: “”, overwrites user 1’s changes!)

In the sample above, user 2 will always overwrite the key/value stored by user 1 because Dapr interprets the blank ETag as “there’s no ETag, so I won’t use first-write-wins”.

For OCC to work in all scenarios, we need a distinction between a blank ETag and a missing ETag.

Blank ETag = first-write-wins, only save state if it doesn’t exist yet
Missing ETag = last-write-wins

@skyao
Copy link
Member

skyao commented Jan 28, 2021

Blank ETag = first-write-wins, only save state if it doesn’t exist yet
Missing ETag = last-write-wins

I strongly disagree to this solution: make blank and missing to totally different meaning is very very tricky to the user. This is always NOT a good idea.

I summit a issue to discuss this and bring with two better solution: #2739

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants