Open miqrogroove opened 4 years ago
This can happen if two requests are made at the same time - for a page that has never been visited before.
Both will see no existing record, and attempt to insert one.
At the end of the transaction, one will fail.
To reproduce this
1) open the same page in two tabs. 2) delete entries from wt_hit_counter. 3) delete entries from wt_session (because we cache the hit counter in the session). 4) select both tabs (shift-click in firefox) and reload together
This can only happen for two "read-only" requests. An "update" request must follow a read request, which would create the row in wt_hit_counter.
So this bug can never lose data.
We could lock the table - this means we would get a "deadlock" error instead of a "duplicate value" error.
There is logic to handle deadlock errors - the entire request is repeated (up to 3 times).
But this doesn't help. We update the counter at the start of the request, and this simply means that the the second request fails three times before the first request has completed.
We can't move the hit-count logic to the end of the request - we need the number during the request.
Maybe we can split it in two...
Is your database driver compatible with the command INSERT ON DUPLICATE KEY UPDATE
? That is a common way to resolve insert races.
I don't think so.
We use Laravel's query builder, and it implements it as select + insert/update.
Presumably because not all databases support it.
Although that still wouldn't help.
Both would try to insert, because we use transactions, and the statement wouldn't know if the row exists until we commit the transaction
If I disable the hit counter setting, does that stop the queries and errors? Or will it still count the hits internally?
If you disable the module, it will stop counting hits.
If the module is enabled, but the footer is disabled, then it will count hits, but not display them.
I would either commit and retry once per request, or commit and immediately ignore any errors. Hit counting should be a very low priority DB use and should not lock the table.
I have been trying to solve this using "pre-emptive locking" - e.g. locking a row which does not (yet) exist.
This is allowed in "Standard SQL", but MySQL has a very old bug which means that it does not work:
https://bugs.mysql.com/bug.php?id=25847
It may work in other databases, such as Postgres - but I don't have a postgres database on my dev machine.
I could lock the entire table - but the performance hit would be unacceptable.
The only solution might be a try/catch block which ignores all errors. But I really don't like doing that.
You need an unusual set of circumstances to trigger this, plus some careful timing.
Maybe just mark this one "wontfix`?
There are almost certainly many other similar cases, for example, writing to all the wt*settings
tables.
Up to you, but I would research this a bit more. You're designing an asynchronous interface for a multi-user platform and query races are a fact of life here. I would be shocked if your database driver doesn't have a way to handle this.
It might even be as simple as checking how many rows were inserted in the catch block and then adding the update query.
Just a note that these kinds of errors occur in other places as well, e.g. in Place.php, see this issue.
(in this case triggered by a custom module handling a place multiple times concurrently, but the actual problem is in the core code - the insert isn't safe wrt concurrency)
I get the error lately in testing for individuals, mediaviewer and places.
The same error or a similar error.
This error is in the hit-counter module. The places page does not use the hit-counter.
I get a similar error: Integrity constraint violation: 1062 Duplicate entry.
To add some context: I just figured out that my mouse is worn out and sometimes sending multiple clicks while the button is pressed. So that's one way to encounter the problem.
Something broke rather dramatically when I clicked on a link to a family just now (my F353).
The error is transient or one time only. No further problem after refresh.
webtrees v2.0.3