Hi,
I have a client system timestamp consistency problem that's puzzling me and I'm not really sure what a good solution strategy would be:
I am testing a Python based backend with Cassandra DB through its Web API, e.g. "create a user A", "get data for user A". The test runner goes through the following simplified sequence:
- call Web API to create the user and extract the new user_id from the response. The backend creates a new user row with an empty permissions column of type MAP
- connect directly to Cassandra with an INSERT to add a new permission to the permissions column
- call the Web API to "get data" for user A
- occasionally receive a 401 permission denied error
(Note: Cassandra 2.1.17, cassandra_driver==3.9.0, Python 2.7.x)
After some research and try and error I found that this happens when the INSERT permission column timestamp is less than the INSERT user row with all columns timestamp (or rather the client system time during the time the CQL is executed).
- The backend running the Web API has a system timestamp T1 when it makes the user row INSERT. I can read the timestamp later using a SELECT user_id, WRITETIME(user_name) FROM users WHERE user_id='abc';
- The test runner adding an element to the user row permissions MAP has a system timestamp T2 when it makes the INSERT.
- If T2 < T1, a subsequent SELECT does not show the added permission
If the test runner's internal timestamp lags more than the request roundtrip time behind the Web API server, the permission INSERT is silently ignored (more precisely: completed but never read) because on read, Cassandra treats the row INSERT (which defined ALL columns including the empty permissions MAP) as more recent. If timestamps are somewhat identical, then the HTTP request roundtrip latency usually leads to monotonously increasing client timestamps and no error. The problem is pronounced here because the test runner uses a somewhat different system setup than the backend under test, and because it does a very fast create-update cycle on the same row. Ideally all clients would be NTP time synced but they are not.
A few notes:
- The above flow is just an example for reproducing the error. Other non-test, non-permission, non-MAP-type related use cases show the same behavior.
- I do not want to add a test endpoint to the backend that just inserts a specific permission to the user row because this is not part of the production server.
- This does not occur in production because there is no use case when a row is inserted and immediately modified by a client on a different host. I am very aware that Cassandra is an eventually consistent, strongly distributed, non-centralized system. It works quite well for us to date but I'd like to find a way around the testing problem. Also, production hosts are more strictly NTP synced, so this limits the risk further.
- For testing it's using a single node Cassandra cluster, but 2 different hosts for test runner and backend.
My question is: is there a way to instruct the cassandra_driver to generate the internal (column writetime) timestamp server side (by the Cassandra node)? I assume that all Cassandra nodes are much better synced than the the clients. Would that solve anything or make it worse?
Are there any other strategies?
Thanks,
Michael