-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
feat(clustering) atomic exports of declarative configs with postgres #8586
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I wonder if this is really safe. Since :each
might yield, would this cause some potential inconsistencies with, say, concurrent Admin API calls?
For example, worker 1 is exporting, it begins the transaction and yields, worker 2 inserts a new Service
, then user issues a request to insert a Route
for this new Service
and got forwarded to worker 1, this would fail because worker 1 is still inside a transaction and can not see the new Service
yet.
Still, I trust this makes the situation better, but wondering if there is something else we can also do to mitigate this potential race. Maybe use a special DB connection just for db_export
and do not share it with the rest of the query?
b0725a3
to
634633e
Compare
@lukego conducted a performance test with this branch, and it seems to be working as indented. |
Note: I have only checked the error logs so far. This change made a big improvement there. I will now test for effects on memory and performance and request errors and ... |
There are multiple TCP connections to the database and I don't think (I'm not 100% sure) there are concurrent queries on the same TCP connection. If a Service was created previously, an insert query for a route subsequently will see the previously created Query, else that would violate serialization. The transaction that is doing the export on worker 1 will not see the route, it may see the Service if that service was created before the transaction began. Historically, Kong has been considered eventually consistent. This change seems to challenge that in some regard (not all). |
@hbagdi I expressed the same concern with @bungle offline regarding mixing up the connection. Another potential issue is that after this change the CP memory usage had an what appears to be a huge increase, so we are holding off this merge for now pending further investigation. I personally think mixing the export connection which requires the transaction with those that doesn't is a slightly dangerous idea for my tase. I would like to see the export happen on it's dedicated connection instead. |
I am pretty sure this has nothing to do with this change. If memory is increased anywhere, it would happen on Postgres. |
This is a bit opaque because of design of
TL;RD; our goal has never been "eventual" when that just does not work, as is the case here (on big databases a huge exports that can not always be imported, and which can lead to situation where things are not even fixed eventually). This has nothing to do with eventual vs. non-eventual. This is about making exports that can be imported. Right now in environments where there are simultaneous changes (e.g. admin api calls) and exports (as it is a case with hybrid exports, and also to some extend with kong config db_export), there is a chance that we export data that is never going to import as the validation on import will fail as the data is exported on different tables with different state of database, and that will lead to foreign key violations, but to some other smaller issues too. What this change does is:
And that's it. |
@dndx, It is not shared. We store it in |
634633e
to
154a76e
Compare
@hbagdi, The eventual consistency has been just a tool to avoid bigger issues. If there were no related issues (e.g. huge latencies in proxy or other operations), nothing would have been designed to be eventual, and everything would share the same state all the time, and in real-time. But because that is not possible or at least very very hard, we have decided to do some tasks eventually. Let's take the CP-DP config export scenario:
Now everything here can be seen eventual. Only thing that this PR touches is 5. where it creates that lua table using a single TCP connection to postgres database and runs queries in snapshot isolation (it does not lock writes nor other reads) — there is nothing eventual with this function, only thing it results is a lua table, that better be something that can be imported by DPs. This is all handled by postgres itself. Worth to mention that it is not necessary to run this through single connection. Other postgres connections can also join the same snapshot and make queries at the same time (with proper SQL statements executed to do so, including some Postgres only functions). This way you can even parallelise the config export. It is more involved design though as more queries need to be executed and we need a master connection to close when everything is done. But it is possible. I didn't want to go this route as I am not sure is the parallelisation of export a good idea, and when that is not decided, it is better to just run it through a single connection as it is basically just DAO queries wrapped with BEGIN and COMMIT (in certain isolation level). |
It is not mixed, because:
Admin API calls will not share the same |
I was thinking about making this a |
While I don't disagree what you are saying. I hope the above answers assure you that this is fine. The mentioned memory increase by 40x in @lukego's test should be repeated as I can hardly believe this causes any memory pressure on Kong itself. It probably should make it less so, as it basically uses a fewer calls, but it is always possible that there is a bug somewhere in When I say, I don't disagree, it is about the design of Kong |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took another look at the logic stated here and inside the connector
parent class, and it should be fine. Since the connector is stored on the ngx.ctx
, and we do not call db.connector:setkeepalive()
until the export finishes, there should be no chance for the export to be interrupted. Nice work @bungle !
Just left some minor comment, but otherwise LGTM.
### Summary Adds support for atomic exports of declarative configs with Postgres.
154a76e
to
94aab9c
Compare
Summary
Adds support for atomic exports of declarative configs with Postgres.