-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
db.session.commit() is not performed at the end of requests (prev "Confirmation of users") #58
Comments
I'm currently having a similar issue but a little bit different. I just got past Chapter 9 and now my confirmation emails only work if the user is already signed in. If the user is not already signed in, the confirmation email link just redirects to the login screen and asks the user to "Please log in to access this page." Of course, when the login they reach the unconfirmed page and can ask for a new email but that just seems rather unnecessary. I'm probably missing something rather stupid but I just can't seem to figure out why the confirmation emails only work if I'm already logged in. It's as if the confirm() function is getting skipped because of the @login_required. Everything else seems to work great. |
@chelseanderby the login process is required by design. After you follow the confirmation link and login (if needed), you will see a flash message saying that the account is confirmed and you are then redirected to the home page. If the confirmation is not successful you will see a different flash message and still be redirected to the home page. By the way, I'd guess that in most of the cases (say when the confirmation email arrives immediately) the user is already logged in when they click on the link, because they were logged in to trigger the sending of the email. If you are instead redirected to the Let me add an important piece of information (I forgot to add that before, sorry). If I run the same application on localhost it works fine. The misbehaviour is only seen when the app runs on heroku. |
@lucanaso Are you using a Postgres database on Heroku, or something else? |
@miguelgrinberg yes I am using "Heroku Postgres", plan "Hobby dev", version 9.4.4. |
Hmm. Have you made any changes to the configuration? In particular, the |
The I forked the latest available commit 17d from your repository, |
@lucanaso Thanks for pointing that out to me. I guess that's just not how I'd imagine it would work because it seems a little counter intuitive to me that you would already need to be logged in earlier for the confirmation email to actually work (and from my workflow tests I did, the user would not be already logged in most cases since following the register path did not logged them automatically). So I was wondering as to why when I login after the confirmation email link, I get directed to the unconfirmed page rather than the main page? Only when I am already logged in does the confirmation email work. I mean, I'd guess that's not how it's setup to work but if so, how could I change that? Of course, it's probably something I'm doing wrong, I just want to make sure I actually understand what the app is supposed to do because before I bash my head on the wall for awhile trying to figure it out because my app is almost identical at this stage, minus a couple changes. EDIT: Figured it out. I'm not using Bootstrap for quick forms and I was setting up my form incorrectly so it was redirecting wrong. |
In theory it should not make a difference, but could you please try adding a |
Indeed, it did not make any difference. But here I need to ask a side question, before we validate this step. Is this correct? or I have to push to master (which is the current default)? or I have to make the new branch as the default branch? |
News update. I have authenticated a user by hand and then experimented with it. In fact the application always fail to write to the db and gives no error message.
It looks like only the user registration is actually working, which, I believe, is the only one to have an explicit db.session.commit(). Regarding the previous side note. I have made the latest branch as the default one and re-run the same commands as before, but still no difference. |
It looks like the real problem behind this symptom is that I did not see this before in #58 (comment) because the correct way to synch the heroku app to a branch different than master is: @miguelgrinberg @chelseanderby |
I met the same problem.
after every sql it log COMMIT.Everything goes fine.
after every sql it log ROLLBACK. I thought in gunicorn SQLALCHEMY_COMMIT_ON_TEARDOWN=True not worked. |
I just discovered this issue today. |
Guys, I need you to tell me which version of gunicorn you are using. With version 18.0 (the version that I used back when I created this application) everything works for me. With version 19.3 (the latest) I can reproduce the behavior described by @alexli0707 above. Are you all using version 19? Can you try version 18 and let me know if this addresses the problem for you? Thanks! |
I have 19.3. I have not tried 18.0 yet. |
Okay, I think I have figured this thing out, turns out this is a Flask bug. When the app context is popped at the end of the request, Flask calls all the Unfortunately, the logic that the current version of Flask uses to determine if there was an exception is flawed. This has been fixed in master (see the PR) and will be included in the 1.0 release. Now, why this happens with gunicorn v19 but not v18? The sync worker in version 19 throws an exception before it passes control to the Flask application. This exception is handled, so it does not cause any problem other than exposing the Flask bug, which thinks this gunicorn exception was thrown during the request. So the Flask-SQLAlchemy teardown callback is called with the gunicorn exception as an argument, and because of that Flask-SQLAlchemy thinks there was an error and does not commit the session. |
It appears this also prevents users from changing passwords in Flasky. |
I confirm that by using gunicorn 18.0 instead of gunicorn 19.3 (current latest) the issue is not present. Thanks to @miguelgrinberg for finding the solution and everyone else for contributing! |
I guess this is the same issue @jeffjzx and I encountered over a year ago in #11 . My solution was to change the gunicorn worker to async but afterwards I changed it back to the normal sync worker (can't remember excactly why I did that...) and added an |
@miguelgrinberg @lucanaso This issue might be triggered by Flask-SSLify. I just confirmed that by using gunicorn 18.0 with Flask-SSLify 0.1.5 the issue is still there. Then I downgrade Flask-SSLify version to 0.1.4. Then the issue is not present. |
@xvweirong this is a Flask bug, it's already fixed in Flask's master branch. See pallets/flask#1393. |
So I have downgraded my Gunicorn to version 18.0 by removing the most recent version and installing this one by I am using https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-gunicorn-and-nginx-on-ubuntu-14-04 to set up my app and everything works when I run Any ideas? Edit: It seems adding db.session.commit() manually works, but I'm just wondering if anyone has found another way of doing this. |
My guess is that when you run through upstart you are not using the gunicorn installed in your virtualenv. Make sure you use the full path to the gunicorn executable, something like |
Hi Miguel, I can confirm that the bug also affects the version 18.0 of gunicorn:
With or without SSLfy, tested in different environments:
and several databases:
In all these cases, the app seems to work properly (flash messages are displayed to the user) but the data is not (always) saved to DB. Activating the echo of sqlalchemy I note that, in cases of failure to save on the db, a sqlalchemy rollaback is triggered. The most disturbing thing is that all of the above occurs in almost randomly (!!!): the same views and request sometimes save the data and others do not .... I can not understand the cause of this random behavior. The only workarond with these framework's versions appears to be the explicitly add db.session.commit () at the end of each request. The alternative (form me) is to upgrade all the frameworks to the latest version:
Also in this case it seems that the random triggering of rollback are gone and the app seems to work properly. I hope this information will be of help. Greetings |
I am still having this issue on latest of all requirements. Does anyone know of a possible fix or workaround? I have echoing turned on and see rollbacks after almost every query (insert/select/whatever) Edit: heroku postgres as well |
The workaround solution is to explicitly commit the database session at the end of each request. The auto-commit option in Flask-SQLAlchemy is not as reliable as I thought. |
Gotcha, thanks! It is also worth noting that I experienced the same rollback symptoms under the following circumstances:
I'm thinking this has something to do with heroku rather than any application/library logic? This just started happening an hour ago and the affected code has been static for a week or so. @vincentdo this may be of relevance to you. |
@sachabest @miguelgrinberg |
Closing this issue, as it does not apply to the second edition of the book. |
Update - begins
db.session.commit()
is not performed automatically at the end of each request, despite havingSQLALCHEMY_COMMIT_ON_TEARDOWN = True
in config.py.This issue was noted after analysing why user confirmation was not working properly.
The app is hosted on heroku (can be reached here), is using Heroku Postgres and is based on this repository, which is forked from the original 17d.
Update - ends
The confirmation link does not update the
current_user.confirmed
toTrue
and new registered users thus remain unconfirmed.If the model column is changed by hand via shell, then the user is recognised as confirmed and the app behaves as expected.
Here are the results of my analysis.
By examining the logs, the app behaviour is correct. I have the following lines from registration to confirmation of a new user:
2015-08-07T08:50:09.059305+00:00 heroku[router]: at=info method=GET path="/auth/register"
2015-08-07T08:50:23.954973+00:00 heroku[router]: at=info method=POST path="/auth/register"
2015-08-07T08:50:24.181845+00:00 heroku[router]: at=info method=GET path="/auth/login"
2015-08-07T08:50:35.040888+00:00 heroku[router]: at=info method=GET path="/auth/confirm/eyJleHAiOjE0Mzg5NDEwMjMsImlhdCI6MTQzODkzNzQyMywiYWxnIjoiSFMyNTYifQ.eyJjb25maXJtIjoxfQ.7hMYjfDmmwE_8c3kkw7jSdIRLWV-bPSJCCuYsy7bPeY"
2015-08-07T08:50:35.259303+00:00 heroku[router]: at=info method=GET path="/auth/login?next=%2Fauth%2Fconfirm%2FeyJleHAiOjE0Mzg5NDEwMjMsImlhdCI6MTQzODkzNzQyMywiYWxnIjoiSFMyNTYifQ.eyJjb25maXJtIjoxfQ.7hMYjfDmmwE_8c3kkw7jSdIRLWV-bPSJCCuYsy7bPeY"
2015-08-07T08:51:04.677048+00:00 heroku[router]: at=info method=POST path="/auth/login?next=%2Fauth%2Fconfirm%2FeyJleHAiOjE0Mzg5NDEwMjMsImlhdCI6MTQzODkzNzQyMywiYWxnIjoiSFMyNTYifQ.eyJjb25maXJtIjoxfQ.7hMYjfDmmwE_8c3kkw7jSdIRLWV-bPSJCCuYsy7bPeY"
2015-08-07T08:51:04.947112+00:00 heroku[router]: at=info method=GET path="/auth/confirm/eyJleHAiOjE0Mzg5NDEwMjMsImlhdCI6MTQzODkzNzQyMywiYWxnIjoiSFMyNTYifQ.eyJjb25maXJtIjoxfQ.7hMYjfDmmwE_8c3kkw7jSdIRLWV-bPSJCCuYsy7bPeY"
2015-08-07T08:51:05.156445+00:00 heroku[router]: at=info method=GET path="/"
2015-08-07T08:51:05.868130+00:00 heroku[router]: at=info method=GET path="/auth/unconfirmed"
From what I understand, the
auth.before_app_request
is redirecting the user to the unconfirmed page.However the flash message
"You have confirmed your account. Thanks!"
is displayed (see https://github.com/lucanaso/flasky/blob/master/app/auth/views.py#L72), therefore it looks likecurrent_user.confirm
returnsTrue
but it does not update the db (see https://github.com/lucanaso/flasky/blob/master/app/models.py#L147).I have forked the project at commit 17d and after that I have committed only some files for deploying on heroku and requirements.
The app is running on heroku at: https://flasky-forky.herokuapp.com/ you can try it yourself by registering and trying to confirm.
What can be the cause of the user not being confirmed?
The text was updated successfully, but these errors were encountered: