FooSoft / anki-connect

Anki plugin to expose a remote API for creating flash cards.
https://foosoft.net/projects/anki-connect/
Other
1.91k stars 217 forks source link

Server stops responding if client disconnects abruptly #209

Closed kanjieater closed 3 years ago

kanjieater commented 3 years ago

By starting requests and then cancelling them Anki Connect will eventually stop responding.

Then while anki connect is frozen, after about 5-10mins I got this error from ankiconnect in anki: ConnectionResetError: [WinError 10054] An Existing connection was forcibly closed by the host

That error popup then crashed anki and I couldn't even copy the text out of the error box, and had to copy it by hand while it was frozen - trying to get more detailed crash log, but I'm not sure how if the gui is crashing.

I'm doing basic cardInfo queries over localhost:

{
    "action": "cardsInfo",
    "version": 6,
    "params": {
        "cards": [
            1198884699985,
            1198899930593
        ]
    }
}

If I do multiple of these from the client I'm trying to use (GoldenDict) or open up a couple tabs with postman I can get this to happen. Start a query like the one above then cancel it and start another, eventually ankiconnect and anki freeze. It seems like anki stays working even while ankiconnect is frozen at first, but then both crash. It's easier to reproduce on large queries, so adding more card id's to the above can help reproduce.


    Anki 2.1.22 (0ecc189a) Python 3.8.0 Qt 5.14.1 PyQt 5.14.1
    Platform: Windows 10
    Flags: frz=True ao=True sv=1
    Add-ons, last update check: 2020-11-25 18:28:09

    ===Add-ons (active)===
    (add-on provided name [Add-on folder, installed at, version, is config changed])
    '' ['PassFailJP', 0, 'None', '']
    '' ['ResetEZ', 0, 'None', '']
    '' ['anki_image', 0, 'None', '']
    '' ['autoLapseNewInterval', 0, 'None', '']
    '' ['autobulk', 0, 'None', '']
    '' ['autovocab', 0, 'None', '']
    '' ['bulkclearhtml', 0, 'None', '']
    '' ['jsentencehighlighter', 0, 'None', '']
    '' ['kanjivocab', 0, 'None', '']
    '' ['leech_handler', 0, 'None', '']
    '' ['phonetics', 0, 'None', '']
    '' ['rtk', 0, 'None', '']
    Advanced Browser ['874215009', 2020-03-14T16:25, 'None', '']
    Advanced Copy Fields ['1898445115', 2019-05-05T13:25, 'None', '']
    Anki Simulator ['817108664', 2020-07-21T09:17, 'None', '']
    AnkiConnect ['2055492159', 2020-11-01T18:16, 'None', '']
    AnkiWebView Inspector ['31746032', 2019-05-02T17:43, 'None', '']
    AwesomeTTS Google Cloud Text-to-Speech unofficial ['atts', 2020-09-17T03:41, 'None', '']
    Batch Editing ['291119185', 2020-03-14T16:24, 'None', '']
    Bulk-Generate Japanese Vocab Frequency Ranking ['1612642956', 2019-04-19T14:19, 'None', mod]
    Correct Answers Graph ['993120073', 2020-03-24T07:57, 'None', '']
    Full Screen Toggle ['1612375712', 2018-12-09T14:22, 'None', '']
    Heatmap ['1722241614', 2019-08-18T10:35, 'None', '']
    Hint Hotkeys ['1844908621', 2019-01-26T15:50, 'None', '']
    HoochieMama Randomize Rev Queue ['1460733408', 2020-06-03T08:17, 'None', '']
    Japanese Bulk Support ['bulk_japanese', 0, 'None', mod]
    Japanese Example Sentences ['2413435972', 2019-04-20T09:16, 'None', '']
    Japanese Pronunciation  Pitch Accent ['932119536', 2019-10-14T16:39, 'None', mod]
    Japanese Support ['3918629684', 2020-03-14T16:24, 'None', mod]
    Kanji Colorizer stroke order diagrams ['1964372878', 2020-11-03T12:33, 'None', '']
    Kanji Grid ['909972618', 2019-01-09T16:40, 'None', '']
    KanjiEaters Bulk-Generate Japanese OnYomi Phonetics ['1226813725', 2019-05-11T16:16, 'None', '']
    MIA Japanese ['MIAJapanese', 2020-03-04T17:19, 'None', mod]
    Maximum image height and width in card editor ['229181581', 2020-03-14T16:24, 'None', '']
    MeCab UniDic Japanese Dictionary ['13462835', 2019-10-13T00:22, 'None', '']
    Migaku Dictionary ['1655992655', 2020-09-16T02:39, 'None', mod]
    More Overview Stats 21 ['738807903', 2019-02-05T07:15, 'None', '']
    MorphMan for Anki 21 ['900801631MM', 2020-03-14T16:24, 'None', '']
    Pretzel Logic Retention Benchmarking ['1168065472', 2020-04-24T22:13, 'None', mod]
    Progress Graphs and Stats for Learned and Matured Cards ['266436365', 2020-03-29T01:26, 'None', '']
    Rebuild All  Empty All for Anki 21 ['1810938259', 2018-12-09T14:12, 'None', '']
    Refocus Card when Reviewing 21 ['1642550423', 2018-12-09T14:11, 'None', '']
    Replay buttons on card ['498789867', 2018-12-09T14:13, 'None', '']
    Search and Replace Tags ['138501288', 2019-05-11T08:15, 'None', '']
    Set Font Size ['651521808', 2019-01-26T11:00, 'None', mod]
    Speed Focus Mode auto-alert auto-reveal auto-fail ['1046608507', 2020-03-14T16:24, 'None', '']
    True Retention by Card Maturity ['923360400', 2019-02-05T07:13, 'None', '']
    load balancer ['1417170896', 2020-07-23T10:37, 'None', '']

    ===IDs of active AnkiWeb add-ons===
    1046608507 1168065472 1226813725 13462835 138501288 1417170896 1460733408 1612375712 1612642956 1642550423 1655992655 1722241614 1810938259 1844908621 1898445115 1964372878 2055492159 229181581 2413435972 266436365 291119185 31746032 3918629684 498789867 651521808 738807903 817108664 874215009 909972618 923360400 932119536 993120073

    ===Add-ons (inactive)===
    (add-on provided name [Add-on folder, installed at, version, is config changed])
    '' ['heatmap', 0, 'None', '']
    '' ['kanjivocab1', 0, 'None', '']
    Color Confirmation ['1084228676', 2020-03-14T16:24, 'None', '']
    Enhance main window ['877182321', 2020-05-03T11:58, 'None', mod]
    Flexible Duplicate Checking ['1587955871', 2020-05-19T01:49, 'None', '']
    Highlight Search Results in the Browser ['225180905', 2019-08-05T17:50, 'None', '']
    Large and Colorful Buttons ['1829090218', 2018-12-03T09:11, 'None', '']
    MIA Japanese Support ['MIAJapaneseSupport', 0, 'None', mod]
    Night Mode ['1496166067', 2020-03-14T16:24, 'None', '']
    Review Heatmap ['review_heatmap', 2018-10-28T17:06, 'None', '']
    Sentence ATM ['1209404305', 2019-11-23T06:38, 'None', '']

Latest AnkiConnect version.

EDIT: I've attached an anki connect log file. Still no luck on getting an Anki Error log. You can see from this log that the card info is absolutely massive. It would be a good idea to allow clients to filter what fields they want to retrieve so they don't waste resources (in my case I don't need front/back but those are the largest!), but I'll open a different ticket for that. ankiconnect.log

kanjieater commented 3 years ago

The crash in anki seems to occur in Advance browser. However it only happens when ankiconnect hangs.

Debug info:
Anki 2.1.22 (0ecc189a) Python 3.8.0 Qt 5.14.1 PyQt 5.14.1
Platform: Windows 10
Flags: frz=True ao=True sv=1
Add-ons, last update check: 2020-11-25 18:28:09

Caught exception:
Traceback (most recent call last):
  File "C:\Users\Micah\AppData\Roaming\Anki2\addons21\2055492159\__init__.py", line 77, in advance
    self.server.advance()
  File "C:\Users\Micah\AppData\Roaming\Anki2\addons21\2055492159\web.py", line 118, in advance
    self.advanceClients()
  File "C:\Users\Micah\AppData\Roaming\Anki2\addons21\2055492159\web.py", line 133, in advanceClients
    self.clients = list(filter(lambda c: c.advance(), self.clients))
  File "C:\Users\Micah\AppData\Roaming\Anki2\addons21\2055492159\web.py", line 133, in <lambda>
    self.clients = list(filter(lambda c: c.advance(), self.clients))
  File "C:\Users\Micah\AppData\Roaming\Anki2\addons21\2055492159\web.py", line 52, in advance
    msg = self.sock.recv(recvSize)
ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host
kanjieater commented 3 years ago

So I tried digging into this. I haven't figured it out how to fix it but the issue is clear.

When a WebClient's socket calls self.sock.recv it get no response from the client (in this situation where a client started a request then disconnected). Then the AnkiConnect's QTimer keeps advancing through the clients before a response comes from WebClient's recv.

I haven't managed to get clientSock.settimeout(5.0) to actually trigger and close the socket but I'm thinking that's what needs to be done.

kanjieater commented 3 years ago

Alright, I did get a working version but it's not very pretty: https://gist.github.com/kanjieater/7ae2e2c930a66604c4e011fe032367a3

The important thing is doing all your work in a single clientSock.recv request so the timeout can actually be applied, that's why the infinite while loop is required. If you keep context switching then it will never finish because the timeout resets on the socket (even if you initialize it outside). https://stackoverflow.com/questions/34371096/how-to-use-python-socket-settimeout-properly

kanjieater commented 3 years ago

Would that gist suffice as a PR? How can this be moved forward?

FooSoft commented 3 years ago

Please create a clean PR for this, and if everything looks good I'll merge it in.