GenericDaoBase.persist() leaves the caller's transaction unbalanced when an insert throws
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 48/100
Línea de trabajo
Comience rastreando GenericDaoBase.persist() y el comportamiento de anidamiento y commit de TransactionLegacy en framework/db; después, inspeccione UsageManagerImpl.createHelperRecord() y createVolumeHelperEvent() como ruta de reproducción. Confirme que un fallo de inserción no pueda dejar desequilibrada la transacción del llamador ni hacer que su commit posterior no haga nada silenciosamente, mientras que los llamadores que gestionan EntityExistsException reciban el comportamiento de error previsto.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
problem
Split out of #13399 at @DaanHoogland's request.
GenericDaoBase.persist() does not own a transaction, it joins the caller's via TransactionLegacy.currentTxn() and calls txn.start(), which pushes a START_TXN nesting level. On the SQLException path it never reaches txn.commit(), and there is no finally, so that nesting level is leaked:
final TransactionLegacy txn = TransactionLegacy.currentTxn(); // the CALLER's transaction
try {
txn.start(); // pushes a START_TXN nesting level
...
pstmt.executeUpdate(); // throws
...
txn.commit(); // never reached
} catch (final SQLException e) {
logger.error("DB Exception on: " + pstmt, e);
handleEntityExistsException(e); // throws EntityExistsException
throw new CloudRuntimeException("Unable to persist on DB, due to: " + e.getLocalizedMessage());
}
// no finally, the pushed nesting level is never released
Consequence: the caller's own commit() then finds the transaction unbalanced and silently no-ops, logging only:
WARN [db.Transaction.Transaction] txn: Commit called when it is not a transaction:
(TransactionLegacy.commit() — if (!_txn) { LOGGER.warn(...); return false; })
Everything in that transaction is discarded while the caller believes it committed.
Why it matters beyond one call site: callers that deliberately catch EntityExistsException in order to log-and-continue cannot actually continue, because the enclosing transaction is already unrecoverable. UsageManagerImpl.createHelperRecord() is one such caller, and in #13399 this is what converts a single constraint violation into permanent usage-aggregation failure rather than one skipped record.
versions
Observed on CloudStack 4.22.1.0 (EL9 packages), MySQL 8.x / InnoDB.
This is a code-level defect in framework/db rather than an environment-specific one; the code path is not version-specific and hypervisor/storage/network are not relevant.
The steps to reproduce the bug
- On 4.22.1.0 with the Usage Server enabled, deploy an instance. Its ROOT volume produces a
VOLUME.CREATEusage event carryingvm_id. UsageManagerImpl.createVolumeHelperEvent()performs twopersist()calls sharing(volume_id, created); the second violatesusage_volume's unique key (see #13399).createHelperRecord()catches the resultingEntityExistsExceptionand logs a warning, intending to continue.- Observe
txn: Commit called when it is not a transactionshortly afterwards, and that theprocessedflags set for that batch of events incloud_usage.usage_eventwere never committed.
Any caller that hits a constraint violation inside a transaction it owns should show the same behaviour, #13399 is simply a case where it happens on every VM deployment.
What to do about it?
Release the nesting level in a finally, and/or mark the transaction rollback-only so callers receive a real failure instead of a silent no-op.
Either way this needs someone familiar with TransactionLegacy's nesting semantics, since GenericDaoBase backs every DAO in the codebase. I'm raising it rather than proposing a patch.
- Lenguaje dominante
- Java
- Estrellas
- 3.1k
- Forks
- 1.4k
- Merge medio
- 6 d 20 h
- PR fusionados (30 d)
- 27
Guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de apache/cloudstack
-
bug
Dificultad 1/5 Menos de una hora Aptitud para principiantes 90/100
apache/cloudstack#14222 ·
-
create-kubernetes-binaries-iso.sh builds the ISO without setting a volume ID on EL8 based os's Abiertobug component:kubernetes
Dificultad 1/5 Menos de una hora Aptitud para principiantes 88/100
apache/cloudstack#14180 ·
-
bug component:projects component:UI
Dificultad 1/5 Menos de una hora Aptitud para principiantes 88/100
apache/cloudstack#14070 · 5 comentarios ·
-
component:backup
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
apache/cloudstack#14013 ·
-
KVM agent fails to connect to Ceph RBD storage pool after upgrading Ceph client to Tentacle 20.2.4 Abiertobug component:ceph
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
apache/cloudstack#13989 · 3 comentarios ·
Todos los issues de apache/cloudstack
Issues similares
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
-
Two open-case totals on one screen: the Programs tile says 15,858 and the nav badge says 15,868 Abiertobug frontend maui-pilot
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
objectionary/eo-graphs#74 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100