[Dennis Allison]
... Conflict errors are not always errors.
At the ZODB level, an unresolved conflict always raises an exception. Whether such an exception is considered to be "an error" isn't ZODB's decision -- that's up to the app. My understanding (which may be wrong) is that Zope tries up to 3 times to perform & commit a given transaction, suppressing any conflict exceptions for the duration, before giving up.
As I understand it, Zope retries when a conflict occurs and usually is able to commit both sides of the conflicting transaction.
Right (although note that there may be more than two sides).
Sometimes Zope cannot commit conflicting transactions--and it is at that point that an error occurs.
Right, Zope eventually gives up on a transaction that keeps on raising conflict exceptions.
There are supposed to be significant changes in the Zope 2.8.4/ZODB 3.4.2 system.
There are. ZODB 3.3 introduced "multiversion concurrency control" (MVCC), which eliminates read conflicts in normal operation.
Read-read conflicts no longer generate conflict errors
Not really: under MVCC, there simply aren't any read conflicts. There may still be write conflicts.
and the retry mechanism has been reworked at the ZODB level to retry once and then raise a POSKEY exception.
Nope, no version of ZODB ever retries a transaction on its own. If an application (like Zope) wants to retry, it's entirely up to it do so.
The optimistic locking used by Zope
ZODB's transactional approach is optimistic, precisely because it _doesn't_ lock objects modified by a transaction. Any number of transactions are free to modify the same object at the same time -- no locking mechanism attempts to stop that. If multiple transactions do modify the same object at the same time, and that object doesn't implement conflict resolution, then only the first transaction to commit its changes to that object can succeed.
can cause problems, particularly when the conflicting method changes external state.
Yes -- but do note it's not a transactional system then (ZODB can roll back all changes _it_ makes, so that a failure to commit does no harm to the database state; external resources that can't "take back" provisional changes are indeed challenging to use in a transactional system).