-
-
Notifications
You must be signed in to change notification settings - Fork 563
Description
Location
File: api_app/engines_manager/models.py
Method: EngineConfig.run()
Description
In EngineConfig.run(), when a job already has a data model, the existing data model is deleted and a new one is assigned. These operations are not atomic. If job.save() fails after the deletion, the job's GenericForeignKey fields (data_model_content_type, data_model_object_id) can end up referencing a deleted BaseDataModel.
This leaves the job in a partially updated and inconsistent state.
Execution Flow
Current order of operations:
- Existing
job.data_modelis deleted from the database - New
BaseDataModelis assigned tojob.data_modelin memory job.save()is called to persist the change
If step 3 fails, the previous deletion is not rolled back.
Trigger Scenarios
job.save() can fail due to normal conditions such as:
- Validation errors
- Signal exceptions
- Database constraint errors
- Connection or transaction failures
Observed Invalid State
After a failed save:
- The old
BaseDataModelis already deleted - The database still contains FK values pointing to the deleted object
- The new
BaseDataModelexists but is not linked to the job - Accessing
job.data_modelraisesDoesNotExistor returnsNone
This results in an inconsistent GenericForeignKey relationship.
Impact
If this occurs, it can lead to:
- Runtime errors when accessing
job.data_model - Engines, pivots, or connectors operating on inconsistent job state
- Queries involving
job.data_modelreturning incorrect results - Orphaned
BaseDataModelrecords accumulating over time - Difficult debugging due to silent data corruption
Expected Safe Behavior
The update should be atomic, so that either:
- The old data model remains until the new one is safely saved, or
- Partial state is prevented if the operation fails
This ensures job.data_model always references a valid object.
Notes
This issue is about atomicity and data integrity, not about changing intended behavior. The exact solution approach is open for discussion. Scope is limited to EngineConfig.run().