Fix transaction abort error on user loading (4.1.1 update issue)

- Add transaction error handling to load_user function
- Create safe_query utility for safe database query execution
- Update test authentication helper to use safe query pattern
- Add comprehensive troubleshooting guide for transaction errors

Fixes issue where failed database transactions would cause
'current transaction is aborted' errors when loading users.
The fix automatically rolls back failed transactions and retries
queries, preventing application crashes.
This commit is contained in:
Dries Peeters
2025-11-29 14:58:43 +01:00
parent ce81852d2e
commit c3e3005baf
3 changed files with 260 additions and 4 deletions

View File

@@ -463,13 +463,20 @@ def create_app(config=None):
try:
if app.config.get("TESTING"):
from flask_login import current_user, login_user
from app.utils.db import safe_query
if not getattr(current_user, "is_authenticated", False):
uid = session.get("_user_id") or session.get("user_id")
if uid:
from app.models import User
user = User.query.get(int(uid))
try:
user_id_int = int(uid)
except (ValueError, TypeError):
user = None
else:
user = safe_query(lambda: User.query.get(user_id_int), default=None)
if user and getattr(user, "is_active", True):
login_user(user, remember=True)
except Exception:
@@ -479,10 +486,16 @@ def create_app(config=None):
# Register user loader
@login_manager.user_loader
def load_user(user_id):
"""Load user for Flask-Login"""
"""Load user for Flask-Login with proper transaction error handling"""
from app.models import User
from app.utils.db import safe_query
return User.query.get(int(user_id))
try:
user_id_int = int(user_id)
except (ValueError, TypeError):
return None
return safe_query(lambda: User.query.get(user_id_int), default=None)
# Check if initial setup is required (skip for certain routes)
@app.before_request

View File

@@ -1,8 +1,57 @@
from typing import Optional, Dict, Any
from typing import Optional, Dict, Any, Callable, TypeVar
from flask import current_app
from sqlalchemy.exc import SQLAlchemyError
from app import db
T = TypeVar('T')
def safe_query(query_func: Callable[[], T], default: Optional[T] = None) -> Optional[T]:
"""Execute a database query with automatic transaction rollback on failure.
This function handles the case where a transaction has been aborted by PostgreSQL
(e.g., due to a previous failed query) by rolling back and retrying the query.
Args:
query_func: A callable that executes the database query
default: Optional default value to return if query fails (default: None)
Returns:
The result of the query, or the default value if query fails
Example:
user = safe_query(lambda: User.query.get(user_id))
"""
try:
return query_func()
except (ValueError, TypeError) as e:
# Invalid input - don't retry
current_app.logger.debug(f"Query failed with invalid input: {e}")
return default
except SQLAlchemyError as e:
# Database error - try to rollback and retry
try:
db.session.rollback()
return query_func()
except Exception as retry_error:
# Retry also failed - rollback again and return default
try:
db.session.rollback()
current_app.logger.warning(
f"Query failed after rollback retry: {retry_error} (original: {e})"
)
except Exception:
pass
return default
except Exception as e:
# Unexpected error - rollback and return default
try:
db.session.rollback()
current_app.logger.warning(f"Unexpected error in safe_query: {e}")
except Exception:
pass
return default
def safe_commit(action: Optional[str] = None, context: Optional[Dict[str, Any]] = None) -> bool:
"""Commit the current database session with robust error handling.

View File

@@ -0,0 +1,194 @@
# Troubleshooting: Transaction Aborted Error (4.1.1 Update)
## Problem
When updating to version 4.1.1, users may encounter the following error:
```
sqlalchemy.exc.InternalError: (psycopg2.errors.InFailedSqlTransaction)
current transaction is aborted, commands ignored until end of transaction block
```
This error typically occurs when:
1. A database migration fails partway through execution
2. A previous SQL query in the same transaction failed
3. The transaction wasn't properly rolled back after the failure
4. Subsequent queries are attempted in the failed transaction
## Root Cause
PostgreSQL aborts a transaction when any SQL statement fails. Once aborted, all subsequent SQL commands in that transaction will fail with "current transaction is aborted" until you explicitly rollback or commit (which also rolls back).
This can happen during:
- Application startup when migrations are run
- Regular application operations if an error occurs
- Database connection issues
## Immediate Solution
### Option 1: Restart the Application (Recommended)
The simplest solution is to restart your application container:
```bash
docker-compose restart app
```
This will:
- Rollback any failed transactions
- Re-establish fresh database connections
- Allow the application to continue normally
### Option 2: Manual Database Rollback
If restarting doesn't work, manually rollback the transaction:
**Using psql:**
```bash
# Connect to your database
docker-compose exec db psql -U timetracker -d timetracker
# Rollback any failed transactions
ROLLBACK;
# Exit
\q
```
**Using Flask Shell:**
```bash
docker-compose exec app flask shell
# In the shell:
from app import db
db.session.rollback()
exit()
```
### Option 3: Check for Failed Migrations
1. Check the current migration status:
```bash
docker-compose exec app flask db current
```
2. Check migration history:
```bash
docker-compose exec app flask db history
```
3. If migrations failed, check the logs:
```bash
docker-compose logs app | grep -i migration
```
4. Try running migrations again:
```bash
docker-compose exec app flask db upgrade
```
## Code Fix (Already Implemented)
The issue has been fixed in the codebase by adding proper transaction error handling to the `load_user` function and other critical database query points. The fix includes:
1. **Automatic transaction rollback** when queries fail
2. **Retry logic** after rolling back failed transactions
3. **Graceful error handling** that doesn't crash the application
### What Was Fixed
1. **User Loader (`app/__init__.py`)**: The `load_user` function now handles failed transactions by rolling back and retrying
2. **Test Authentication Helper**: The test user authentication helper also includes transaction error handling
3. **New Utility Function**: Added `safe_query()` utility function for reusable safe query execution
### Using the Safe Query Utility
The new `safe_query()` utility can be used for any database query that might fail:
```python
from app.utils.db import safe_query
# Example: Safe user query
user = safe_query(lambda: User.query.get(user_id), default=None)
# Example: Safe query with custom default
project = safe_query(lambda: Project.query.filter_by(id=project_id).first(), default=None)
```
## Prevention
To prevent this issue in the future:
1. **Always backup before migrations:**
```bash
pg_dump -U timetracker timetracker > backup_$(date +%Y%m%d).sql
```
2. **Run migrations in a transaction-safe environment:**
```bash
# Check status first
flask db current
# Run migrations
flask db upgrade
# Verify success
flask db current
```
3. **Monitor application logs** for database errors:
```bash
docker-compose logs -f app | grep -i "database\|transaction\|rollback"
```
4. **Use the safe_query utility** for critical queries that might fail
## Verification
After applying the fix or restarting, verify everything is working:
1. **Check application logs:**
```bash
docker-compose logs app | tail -50
```
2. **Test database connection:**
```bash
docker-compose exec app flask shell
# In shell:
from app import db
db.session.execute(db.text('SELECT 1'))
exit()
```
3. **Verify user login:**
- Try logging into the application
- Check that you can access user-specific pages
## Related Files
- `app/__init__.py`: User loader with transaction error handling
- `app/utils/db.py`: Safe query utility function
- `app/routes/client_portal.py`: Example of transaction error handling pattern
## Additional Resources
- [PostgreSQL Transaction Documentation](https://www.postgresql.org/docs/current/tutorial-transactions.html)
- [SQLAlchemy Session Management](https://docs.sqlalchemy.org/en/20/orm/session_basics.html)
- [Flask-Migrate Documentation](https://flask-migrate.readthedocs.io/)
## Support
If you continue to experience issues after following these steps:
1. Check the application logs for detailed error messages
2. Verify your database connection string is correct
3. Ensure all migrations have been applied successfully
4. Check for any database constraints or foreign key issues
For additional help, please provide:
- Full error traceback from logs
- Output of `flask db current`
- Database version (`docker-compose exec db psql --version`)
- Application version (check `setup.py` or `docker-compose.yml`)