mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2025-12-31 00:09:58 -06:00
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:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
194
docs/TROUBLESHOOTING_TRANSACTION_ERROR.md
Normal file
194
docs/TROUBLESHOOTING_TRANSACTION_ERROR.md
Normal 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`)
|
||||
|
||||
Reference in New Issue
Block a user