confusion about parallelization/thread safety in web server
I'm a bit confused about how the web server behaves with multiple simultaneous incoming requests. I'm testing mostly default settings and to me it seems the log messages suggest a single worker gets be spawned, it prints this 5 times:
2025-[...] VERBOSE Parallelization DISABLED
In terms of the "workers" it's not quite clear to me how many actually get spawned and if there are multiple threads:
2025-09-26 12:11:58,936 DEBUG DBSession.engine_factory(<seedpsd.settings.workers.Settings object at 0x7fce6122b410>, True, True, False, None, None, None)
2025-09-26 12:11:58,936 DEBUG Creating a NullPool with: {'execution_options': {'postgresql_readonly': True, 'isolation_level': 'AUTOCOMMIT', 'schema_translate_map': None}, 'pool_pre_ping': True, 'pool_recycle': -1, 'poolclass': <class 'sqlalchemy.pool.impl.NullPool'>}
2025-09-26 12:11:58,937 DEBUG SeedPsdWorkerPPSD.__init__(<seedpsd.settings.workers.Settings object at 0x7fce6122b410>, Engine(postgresql+psycopg://seedpsd:***@xxx/seedpsd))
2025-09-26 12:11:58,937 DEBUG SeedPsdWorker.__init__(<seedpsd.settings.workers.Settings object at 0x7fce6122b410>, Engine(postgresql+psycopg://seedpsd:***@xxx/seedpsd), False, None)
2025-09-26 12:11:58,937 VERBOSE Parallelization DISABLED
2025-09-26 12:11:58,937 DEBUG SeedPsdWorkerPlot.__init__(<seedpsd.settings.workers.Settings object at 0x7fce6122b410>)
2025-09-26 12:11:58,937 DEBUG SeedPsdWorker.__init__(<seedpsd.settings.workers.Settings object at 0x7fce6122b410>, None, False, None)
2025-09-26 12:11:58,938 VERBOSE Parallelization DISABLED
2025-09-26 12:11:58,938 DEBUG SeedPsdWorkerContent.__init__(<seedpsd.settings.workers.Settings object at 0x7fce6122b410>, Engine(postgresql+psycopg://seedpsd:***@xxx/seedpsd))
2025-09-26 12:11:58,938 DEBUG SeedPsdWorker.__init__(<seedpsd.settings.workers.Settings object at 0x7fce6122b410>, Engine(postgresql+psycopg://seedpsd:***@xxx/seedpsd), False, None)
2025-09-26 12:11:58,938 VERBOSE Parallelization DISABLED
2025-09-26 12:11:58,938 DEBUG SeedPsdWorkerValue.__init__(<seedpsd.settings.workers.Settings object at 0x7fce6122b410>)
2025-09-26 12:11:58,938 DEBUG SeedPsdWorker.__init__(<seedpsd.settings.workers.Settings object at 0x7fce6122b410>, None, False, None)
2025-09-26 12:11:58,938 VERBOSE Parallelization DISABLED
2025-09-26 12:11:58,992 INFO Serving on http://0.0.0.0:8003
It seems that one single SeedPsdWorkerPlot
plotting worker gets spawned but I seem to end up with roughly 10 threads, and if I do 5 simultaneous requests it seems these get worked on in parallel and also plotted in parallel and it seems the plotting gets confused with some matplotlib artists ending up in the wrong figures. For example, the most obvious for me is that some of the plots are missing the footer info line, and it looks like in some plots there are multiple footer lines on top of each other.
I can see in the code that some matplotlib calls are not done on the actual artists, but using the pyplot
convenience interface, which isn't thread safe since it acts on the currently active figure.
I failed to find info on the parallelization/threading options/config/defaults in the docs, maybe I didn't look in the right places?
I could be wrong, I'm working on assumptions here, but it seems to me that there is a single plotting worker, but that it works on individual plot calls not sequentially stringing together all plot calls for a single plot but mixing these calls as previous calls get finished.
In any case, all pyplot
convenience usage that relies on what figure/axis/artist is currently "active" should be avoided I think.
I'm not really sure what this implies for the spectral_estimation.py
from obspy, most of the plt.
calls are probably in if/else clauses for image output to file and might not get hit depending on how it is used here, but it might need checking if it needs modifying too, since it was not written with this kind of usage in mind.
Here's a grep for pyplot use on the code, most of these calls might want to get replaced I think, in the case I noticed the plt.figtext()
in workers/plot.py
was the culprit..
seedpsd/workers/plot.py
429: # plt.tight_layout(rect=[0.08, 0.1, 1.0, 0.95])
445: plt.annotate(
486: footer = plt.Rectangle(
516: plt.figtext(
576: cmaps = plt.colormaps()
seedpsd/tools/ppsd.py
152: cb = plt.colorbar(ppsd, ax=ax)
168: plt.savefig(filename)
171: plt.draw()
seedpsd/tools/spectral_estimation.py
1620: fig, ax = plt.subplots()
1643: cb = plt.colorbar(quadmesh, ax=ax)
1672: plt.savefig(filename)
1673: plt.close()
1675: plt.draw()
1676: plt.show()
1678: plt.draw()
1769: fig, ax = plt.subplots()
1825: plt.savefig(filename)
1826: plt.close()
1828: plt.draw()
1829: plt.show()
1831: plt.draw()
1915: fig = plt.figure()
2069: plt.savefig(filename)
2070: plt.close()
2072: plt.draw()
2073: plt.show()
2075: plt.draw()
2114: cb = plt.colorbar(ppsd, ax=ax)
2133: plt.savefig(filename)
2136: plt.draw()
2158: fig, ax = plt.subplots()
2164: plt.draw()
2166: plt.savefig(filename)
2167: plt.close()
2169: plt.show()