Asyncio RuntimeError: Event loop is closed flask app - python-3.6

I have a flask app hosted on gunicorn webserver and trying to parallelize long running I/O bound task "somemethod" as shown below. However often times (not always) it throws error "Event loop is closed". What could be the reason for the error happening randomly ?
Removing loop.close() does the fix the errors, but I am not sure if there would be memory leak in the python worker process.
async def somemethod():
""" Do some work """
app.route('/hello', method=['POST'])
def sayhello():
loop = asyncio.new_event_loop()
try:
asyncio.set_event_loop(loop)
future = asyncio.ensure_future(somemethod)
loop.run_until_complete(future)
finally:
loop.close()

Related

In Airflow, what could be different between calling "python pppp.py" and in a #task() import pppp?

So I have an Airflow task that is currently a BashOperator() where its bash_command is just "python pppp.py". There is a "__main __" that the only thing it does is calls mycommand(). I tried to switch it to a #task() where I "import pppp" and then call "pppp.mycommand()". My command connects to an internal messaging system. The BashOperator() works but the task() fails to connect. I even kubectl into the pod, and I can start up a python shell, do "import pppp" and call "pppp.mycommand()" and it works. I have confirmed they both ways are using the same image, have all of the same environment variables set to the same value, and such. Obviously there is a difference between the 2 ways. Can anyone think of what is different? THERE IS NO CODE TO SHARE since the issue is with an internal message system that you wouldnt be able to access. I guess I am interested in happens before airflow executes the my_task()
#task(**args)
def my_task():
import pppp
pppp.mycommand()

Python3.8 asyncio behavior difference between Windows and Unix

I am working on script where I will be dealing with huge amount of data to process via python.
I have written a script using asyncio in python3.8 on windows box which is working perfectly fine but when I execute the same script on unix on python3.8 its completing the execution but not terminating the program at the end. Seems like its not release resources/lock.
When I debug further, found that on windows the asyncio uses ProactorEventLoop whereas on Unix it uses _UnixSelectorEventLoop, But not sure if this affect by any means.
I cant share the full script but it follows below structure:
import asyncio
async def myCoroutine():
print("My Coroutine")
try:
loop = asyncio.get_event_loop()
loop.run_until_complete(myCoroutine())
print("Execution Completed")
finally:
print("Closing the loop")
loop.close()
print("loop Closed")
Output:
Execution Completed
Closing the loop
loop closed
But program is not terminating.
Is anyone faced the similar issue before? Any inputs?
Thanks in Advance!!

Errors while using asyncio in Python 3.6 to execute terminal commands

I am using asyncio for the first time and I am getting these two errors:
1) RuntimeError: set_wakeup_fd only works in main thread
2) RuntimeError: Cannot add child handler, the child watcher does not have a loop attached
Usage scenario:
I have to execute two terminal commands, one followed by another. To ensure the second command gets executed only after the first, I'm using asyncio to set the loops and running them.
I am trying to execute a function in this python file using a flask api ultimately.
Has someone come across these errors and can guide me to resolve it?
Any help would be great!
Code:
async def segmentVide(command):
process = await asyncio.create_subprocess_shell(
command,
stdout=asyncio.subprocess.PIPE)
stdout, stderr = await process.communicate()
asyncio.get_child_watcher()
return stdout.decode().strip()
def segmentOutput(folderCreated):
#command = MP4Box terminal command
loop = asyncio.new_event_loop()
asyncio.get_child_watcher().attach_loop(loop)
asyncio.set_event_loop(loop)
coro = loop.run_in_executor(None, segmentVide, command)
loop.run_until_complete(coro)
loop.close()
print('completed')
There is another function executing the other terminal command but the asyncio usage part is the same in it too.

How to find the number of upstream tasks failed in Airflow?

I am having a tough time in figuring out how to find the failed task for the same dag run running twice on same day(same execution day).
Consider an example when a dag with dag_id=1 has failed on the first run (due to any reason lets say connection timeout maybe) and task got failed. TaskInstance table will contain the entry of the failed task when we try to query it. GREAT!!
But, If I re-run the same dag(note that dag_id is still 1) then in the last task(it has the rule of ALL_DONE so irrespective of the whether upstream task was failed or was successful it will be executed) I want to calculate the number of tasks failed in the current dag_run ignoring the previous dag_runs. I came across dag_run id which could be useful if we can relate it to TaskInstance but I could not. Any suggestions/help is appreciated.
In Airflow 1.10.x the same result can be achieved by much simpler code that avoids touching ORM directly.
from airflow.utils.state import State
def your_python_operator_callable(**context):
tis_dagrun = context['ti'].get_dagrun().get_task_instances()
failed_count = sum([True if ti.state == State.FAILED else False for ti in tis_dagrun])
print(f"There are {failed_count} failed tasks in this execution"
The one unfortunate problem is that context['ti'].get_dagrun() does not return instance of DAGRun when running test of a single task from CLI. In the effect, manual testing of that single task will fail but the standard run will work as expected.
You could create a PythonOperator task which queries the Airflow database to find the information you're looking for. This has the added benefit of passing along the information you need to query for the data you want:
from contextlib import closing
from airflow import models, settings
from airflow.utils.state import State
def your_python_operator_callable(**context):
with closing(settings.Session()) as session:
print("There are {} failed tasks in this execution".format(
session.query(
models.TaskInstance
).filter(
models.TaskInstance.dag_id == context["dag"].dag_id,
models.TaskInstance.execution_date == context["execution_date"],
models.TaskInstance.state == State.FAILED).count()
)
Then add the task to your DAG with a PythonOperator.
(I have not tested the above, but hopefully will send you on the right path)

What to do when a py.test hangs silently?

While using py.test, I have some tests that run fine with SQLite but hang silently when I switch to Postgresql. How would I go about debugging something like that? Is there a "verbose" mode I can run my tests in, or set a breakpoint ? More generally, what is the standard plan of attack when pytest stalls silently? I've tried using the pytest-timeout, and ran the test with $ py.test --timeout=300, but the tests still hang with no activity on the screen whatsoever
I ran into the same SQLite/Postgres problem with Flask and SQLAlchemy, similar to Gordon Fierce. However, my solution was different. Postgres is strict about table locks and connections, so explicitly closing the session connection on teardown solved the problem for me.
My working code:
#pytest.yield_fixture(scope='function')
def db(app):
# app is an instance of a flask app, _db a SQLAlchemy DB
_db.app = app
with app.app_context():
_db.create_all()
yield _db
# Explicitly close DB connection
_db.session.close()
_db.drop_all()
Reference: SQLAlchemy
To answer the question "How would I go about debugging something like that?"
Run with py.test -m trace --trace to get trace of python calls.
One option (useful for any stuck unix binary) is to attach to process using strace -p <PID>. See what system call it might be stuck on or loop of system calls. e.g. stuck calling gettimeofday
For more verbose py.test output install pytest-sugar. pip install pytest-sugar And run test with pytest.py --verbose . . .
https://pypi.python.org/pypi/pytest-sugar
I had a similar problem with pytest and Postgresql while testing a Flask app that used SQLAlchemy. It seems pytest has a hard time running a teardown using its request.addfinalizer method with Postgresql.
Previously I had:
#pytest.fixture
def db(app, request):
def teardown():
_db.drop_all()
_db.app = app
_db.create_all()
request.addfinalizer(teardown)
return _db
( _db is an instance of SQLAlchemy I import from extensions.py )
But if I drop the database every time the database fixture is called:
#pytest.fixture
def db(app, request):
_db.app = app
_db.drop_all()
_db.create_all()
return _db
Then pytest won't hang after your first test.
Not knowing what is breaking in the code, the best way is to isolate the test that is failing and set a breakpoint in it to have a look. Note: I use pudb instead of pdb, because it's really the best way to debug python if you are not using an IDE.
For example, you can the following in your test file:
import pudb
...
def test_create_product(session):
pudb.set_trace()
# Create the Product instance
# Create a Price instance
# Add the Product instance to the session.
...
Then run it with
py.test -s --capture=no test_my_stuff.py
Now you'll be able to see exactly where the script locks up, and examine the stack and the database at this particular moment of execution. Otherwise it's like looking for a needle in a haystack.
I just ran into this problem for quite some time (though I wasn't using SQLite). The test suite ran fine locally, but failed in CircleCI (Docker).
My problem was ultimately that:
An object's underlying implementation used threading
The object's __del__ normally would end the threads
My test suite wasn't calling __del__ as it should have
I figured I'd add how I figured this out. Other answers suggest these:
Found usage of pytest-timeout didn't help, the test hung after completion
Invoked via pytest --timeout 5
Versions: pytest==6.2.2, pytest-timeout==1.4.2
Running pytest -m trace --trace or pytest --verbose yielded no useful information either
I ended up having to comment literally everything out, including:
All conftest.py code and test code
Slowly uncommented/re-commented regions and identified the root cause
Ultimate solution: using a factory fixture to add a finalizer to call __del__
In my case the Flask application did not check if __name__ == '__main__': so it executed app.start() when that was not my intention.
You can read many more details here.
In my case diff worked very slow on comparing 4 MB data when assert failed.
with open(path, 'rb') as f:
assert f.read() == data
Fixed by:
with open(path, 'rb') as f:
eq = f.read() == data
assert eq

Resources