#🔒 Function finishes after about a minute for an unknown reason

59 messages · Page 1 of 1 (latest)

eternal raven
#
from mysql import connector
from datetime import datetime
from typing import AsyncGenerator
import asyncio


async def get_data[T, U](query: str, times: int, chunk_size: int = 2000) -> AsyncGenerator[T, U]:
    with connector.connect(host='localhost', user='root', password='*****', database='ffxivcensus') as connection:
        start = datetime.now()
        cursor = connection.cursor()
        cursor.execute(query)

        for i in range(times):
            rows = cursor.fetchmany(size=chunk_size)

            if not rows:
                break

            del rows
            yield cursor.fetchmany(size=chunk_size)

        print(f'Finished Yielding Query {datetime.now() - start}')


async def process() -> None:
    start = datetime.now()
    query = 'SELECT id FROM tblplayers WHERE race <> "N/A"'
    async for j in get_data(query, 5):
        print(f'Finished Getting Data {(datetime.now() - start)}')

    print(f'Finished Everything {datetime.now() - start}')

if __name__ == '__main__':
    asyncio.run(process())

Finished Getting Data 0:00:00.086670
Finished Getting Data 0:00:00.120042
Finished Getting Data 0:00:00.159855
Finished Getting Data 0:00:00.180179
Finished Getting Data 0:00:00.224756
Finished Yielding Query 0:00:00.187877
Finished Everything 0:00:58.940260

I don’t really understand why the Process function takes about a minute to finish despite the function it calls finishes yielding and the for loops finish in less than a second. 

The database is over twenty million entries in the specific table I’m looking at but I’m only grabbing a very small amount of rows
modest yokeBOT
#

@eternal raven

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

wheat olive
#

You should use an async mysql client

#

!pip aiomysql

modest yokeBOT
wheat olive
#

!pip asyncmy

modest yokeBOT
eternal raven
# modest yoke

I’ll try this one out, think I have it installed but never got around to using it

#

…I’ll check both out actually

frank pike
#

Or just don't use async functions and asyncio

wheat olive
#

Why are you calling cursor.fetchmany() twice in the same loop?

eternal raven
#

Had rewritten a little by now. Then was cuz I didn't want to save it as a list but then I just did so cuz honestly it's just better if I do so

frank pike
#

Why is get_data generic?

wheat olive
#

The correct type would be I think AsyncGenerator[Result[Row[tuple[int]]]]

eternal raven
#

Database returns either string or int, probably better if I correct that at some point soon

wheat olive
#

you only select id

eternal raven
#

Only here, as a test. The other functions I plan on using will select race and class levels respectively

wheat olive
#

You should probably loop over rows and yield each one instead of deleting it

eternal raven
#

Yeah, rewrote that. Honestly I have no idea what i was thinking when I wrote that part

wheat olive
#

If you weren't using an async generator, you could do yield from rows

#

Are you still using async?

eternal raven
#

Been trying to find solutions both in async and not async functions

wheat olive
#

Try increasing the chunk size to 100

eternal raven
#
Finished Getting Data 0:00:00.105836
Finished Getting Data 0:00:00.128222
Finished Getting Data 0:00:00.152782
Finished Getting Data 0:00:00.179215
Finished Getting Data 0:00:00.211370
Finished Getting Data 0:00:00.257225
Finished Getting Data 0:00:00.274684
Finished Getting Data 0:00:00.290983
Finished Getting Data 0:00:00.320194
Finished Getting Data 0:00:00.336146
Finished Getting Data 0:00:00.355637
Finished Getting Data 0:00:00.397100
Finished Getting Data 0:00:00.423330
Finished Getting Data 0:00:00.453279
Finished Getting Data 0:00:00.514629
Finished Getting Data 0:00:00.540294
Finished Getting Data 0:00:00.569935
Finished Getting Data 0:00:00.601731
Finished Getting Data 0:00:00.610273
Finished Getting Data 0:00:00.616797
Finished Getting Data 0:00:00.630415
Finished Getting Data 0:00:00.638987
Finished Getting Data 0:00:00.645931
Finished Getting Data 0:00:00.666596
Finished Getting Data 0:00:00.679104
Finished Getting Data 0:00:00.693568
Finished Getting Data 0:00:00.722649
Finished Getting Data 0:00:00.739213
Finished Getting Data 0:00:00.748738
Finished Getting Data 0:00:00.763846
Finished Getting Data 0:00:00.774868
Finished Getting Data 0:00:00.794576
Finished Getting Data 0:00:00.806072
Finished Getting Data 0:00:00.820163
Finished Getting Data 0:00:00.857839
Finished Getting Data 0:00:00.878900
Finished Getting Data 0:00:00.901453
Finished Getting Data 0:00:00.928619
Finished Getting Data 0:00:00.941279
Finished Getting Data 0:00:00.952034
Finished Getting Data 0:00:00.971990
Finished Getting Data 0:00:00.984517
Finished Getting Data 0:00:00.990532
Finished Getting Data 0:00:01.008598
Finished Getting Data 0:00:01.022636
Finished Getting Data 0:00:01.035729
Finished Getting Data 0:00:01.067667
Finished Getting Data 0:00:01.080185
Finished Getting Data 0:00:01.091264
Finished Getting Data 0:00:01.107214
Finished Getting Data 0:00:01.116109
Finished Getting Data 0:00:01.123827
Finished Getting Data 0:00:01.147346
Finished Getting Data 0:00:01.162242
Finished Getting Data 0:00:01.176291
Finished Getting Data 0:00:01.217235
Finished Getting Data 0:00:01.233446
Finished Getting Data 0:00:01.246548
Finished Getting Data 0:00:01.263274
Finished Getting Data 0:00:01.273225
Finished Getting Data 0:00:01.292028
Finished Getting Data 0:00:01.304031
Finished Getting Data 0:00:01.327720
Finished Getting Data 0:00:01.338540
Finished Getting Data 0:00:01.364204
Finished Getting Data 0:00:01.380165
Finished Getting Data 0:00:01.404729
Finished Getting Data 0:00:01.414004
Finished Getting Data 0:00:01.429107
Finished Getting Data 0:00:01.434638
Finished Getting Data 0:00:01.439157
Finished Getting Data 0:00:01.456855
Finished Getting Data 0:00:01.466363
Finished Getting Data 0:00:01.480455
Finished Getting Data 0:00:01.487065
Finished Getting Data 0:00:01.500960
Finished Getting Data 0:00:01.512724
Finished Getting Data 0:00:01.540929
Finished Getting Data 0:00:01.553203
Finished Getting Data 0:00:01.575021
Finished Getting Data 0:00:01.585274
Finished Getting Data 0:00:01.605069
Finished Getting Data 0:00:01.616040
Finished Getting Data 0:00:01.631427
Finished Getting Data 0:00:01.643674
Finished Getting Data 0:00:01.661665
Finished Getting Data 0:00:01.689632
Finished Getting Data 0:00:01.706688
Finished Getting Data 0:00:01.758939
Finished Getting Data 0:00:01.773872
Finished Getting Data 0:00:01.794319
Finished Getting Data 0:00:01.804139
Finished Getting Data 0:00:01.817798
Finished Getting Data 0:00:01.824857
Finished Getting Data 0:00:01.837005
Finished Getting Data 0:00:01.840696
Finished Getting Data 0:00:01.847892
Finished Getting Data 0:00:01.854821
Finished Getting Data 0:00:01.882483
Finished Getting Data 0:00:01.886995
Finished Yielding Query 0:00:01.819275
Finished Everything 0:00:55.213529
#

Okay now I might see an issue here

eternal raven
#

Looking through cProfile's result, this looks like the main culprit?

2   51.193   25.596   51.193   25.596 {method 'free_result' of '_mysql_connector.MySQL' objects}
frank pike
#

Probably you want a with around your connection.cursor

#

@eternal raven ^

eternal raven
#

As in like

with connection.cursor() as cursor:
  pass

?

frank pike
#

No pass

#

You'd put the code that needs the cursor inside the with block

#

@eternal raven did that help?

eternal raven
#

Lemme try to get cProfile to output to a file, I'll run it once I get that up and running

#

So it sped up the yielding but tossed mysql.connector.errors.InternalError: Unread result found immediately before it could finish the process function

#

Might try to create a connection pool

wheat olive
#

You only have one connection

#

Can you share your updated code?

eternal raven
#
async def get_data[T, U](query: str, times: int, chunk_size: int = 2000) -> AsyncGenerator[T, U]:
    with connector.connect(host='localhost', user='root', password='****', database='ffxivcensus') as connection:
        start = datetime.now()

        with connection.cursor() as cursor:
            cursor.execute(query)
            for _ in range(times):
                rows = cursor.fetchmany(size=chunk_size)

                if not rows:
                    break

                yield rows

            print(f'Finished Yielding Query {datetime.now() - start}')


async def process() -> None:
    start = datetime.now()
    query = 'SELECT id FROM tblplayers WHERE race <> "N/A"'
    async for j in get_data(query, 5):
        print(f'Finished Getting Data {(datetime.now() - start)}')

    print(f'Finished Everything {datetime.now() - start}')

if __name__ == '__main__':
    cProfile.run('asyncio.run(process())')
#

I'll worry about the AsyncGenerator annotation later

#

deleting the rows might be pointless

frank pike
#

And the traceback?

#

You leaked your password btw

#

@eternal raven ^

eternal raven
#

I keep forgetting to replace that

#
Traceback (most recent call last):
  File "c:\Users\pengu\Documents\Coding Projects\Python\Data Science Testing\rewrite.py", line 36, in <module>
    cProfile.run('asyncio.run(process())')
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.12_3.12.1008.0_x64__qbz5n2kfra8p0\Lib\cProfile.py", line 18, in run
    return _pyprofile._Utils(Profile).run(statement, filename, sort)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.12_3.12.1008.0_x64__qbz5n2kfra8p0\Lib\profile.py", line 55, in run
    prof.run(statement)
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.12_3.12.1008.0_x64__qbz5n2kfra8p0\Lib\cProfile.py", line 97, in run
    return self.runctx(cmd, dict, dict)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.12_3.12.1008.0_x64__qbz5n2kfra8p0\Lib\cProfile.py", line 102, in runctx
    exec(cmd, globals, locals)
  File "<string>", line 1, in <module>
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.12_3.12.1008.0_x64__qbz5n2kfra8p0\Lib\asyncio\runners.py", line 194, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.12_3.12.1008.0_x64__qbz5n2kfra8p0\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.12_3.12.1008.0_x64__qbz5n2kfra8p0\Lib\asyncio\base_events.py", line 687, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "c:\Users\pengu\Documents\Coding Projects\Python\Data Science Testing\rewrite.py", line 30, in process
    async for j in get_data(query, 5):
  File "c:\Users\pengu\Documents\Coding Projects\Python\Data Science Testing\rewrite.py", line 13, in get_data
    with connection.cursor() as cursor:
  File "C:\Users\pengu\Documents\Coding Projects\Python\Data Science Testing\.venv\Lib\site-packages\mysql\connector\abstracts.py", line 2188, in __exit__
    self.close()
  File "C:\Users\pengu\Documents\Coding Projects\Python\Data Science Testing\.venv\Lib\site-packages\mysql\connector\cursor_cext.py", line 539, in close
    self._cnx.handle_unread_result()
  File "C:\Users\pengu\Documents\Coding Projects\Python\Data Science Testing\.venv\Lib\site-packages\mysql\connector\connection_cext.py", line 1026, in handle_unread_result
    raise InternalError("Unread result found")
mysql.connector.errors.InternalError: Unread result found
```Here's the full traceback
frank pike
#

You probably want .fetchall() not fetchmany

#

You need to consume all results from a cursor before closing it

#

So you should chunk using LIMIT and keyset pagination

eternal raven
#

That resolves the time issue, I'll figure out keyset pagination later tonight if I don't figure it out soon

eternal raven
#

Alright, I'm getting somewhere, thanks for the help @frank pike @wheat olive

wheat olive
#

And still using an async function?

#

(just noticed chunk size is 2000)

eternal raven
#

I’ll probably move it back to a regular function

#

It’s better if I use yield from

frank pike
#

You also might want to use sqlalchemy

eternal raven
#

Wanted to get a handle on the logic in SQL before I moved to SQLAlchemy. I’ll get to that next tho

modest yokeBOT
#
Python help channel closed

This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.