egg-mode-rs/egg-mode

Could use some help understanding error handling

Opened this issue · 2 comments

I was kicking around some code with a friend last night and we were running into a couple of different errors while using your library.

Here's the relevant fragment of the program: https://github.com/bitemyapp/my-limes/blob/master/src/main.rs#L58-L81

I broke out that big dirty manual loop to try to poke at how the CursorIter et al. worked. I have a few questions:

  • Does CursorIter bump the page numbers forward when an error is received?

  • If not, can I just sleep until the epoch timestamp Twitter returns and re-attempt a manual call?

  • If it does, should I clone the CursorIter and hold onto the previous one before call'ing to handle potential errors?

  • Does the iteration API itself do anything to handle errors like RateLimit? It didn't seem like it.

  • Is it kosher to core.run the futures returned by the cursoriter/etc. in a loop?

Sorry for bothering you but it seemed like it'd be best to ask you directly as I thought there must be something I didn't understand in your design intent WRT handling error conditions.

Thank you for your time!

I noticed you posted this on reddit (blame/thank @Havvy for linking that thread to me on IRC), here's what i posted in reply there:


Oh no, sorry for not getting back to y'all! I haven't had a lot of energy recently for rust stuff so i haven't been able to get to your questions. Here's what i can tell you offhand, though:

  • CursorIter will only change the cursor references when it successfully receives a page. However, keep in mind that it will also only update its own cursor references in the Stream implementation. If you're handling the calls manually like that, you'll need to look at resp.response.next_cursorinstead of followers_cursor.next_cursor.
  • I believe the idea behind Twitter returning the timestamp like that is that you can sleep to that point and be fine. IIRC that timestamp is in UTC but otherwise that's the intended purpose.
  • (Looking at it again, it looks like i don't have Clone implemented for CursorIter! I'm not sure whether i can just derive it, though, since it holds the hyper Response, but your idea there won't work, sadly.)
  • The Stream implementation basically just bails when it encounters an error, doing no processing and instead passing it on up the chain immediately. It kinda made the transition from Iterator to Stream kinda poorly, because when the stream returns an error it's basically gone. Your best bet it to do what you wound up doing: don't use the Stream at all, and instead manage the cursor handles manually and use call(). Looking back at this, it's incredibly unfortunate and i should probably rework it somehow. IIRC a forthcoming release of futures changes how futures and streams deal with errors? Maybe i can tap into that whenever that comes around.
  • As far as i can tell, it's totally fine to lean on core.run like that. What that's doing is starting the future in the reactor and sleeping your thread whenever it decides to suspend its task. Thanks to how hyper and tokio are set up, that basically means that your thread is blocking on the network call, just like it were a synchronous call. It's not that great if this is in the middle of a big application and you need to deal with multiple threads' worth of Futures, but i use it in all the examples because it's the way to turn async code into sync code.

egg-mode's model of error handling is "as soon as i get an error, wrap it up and pass it on". As far as the types and stuff, that error enum was decided upon before failure and error-chain were a thing, so it's probably not the most idiomatic any more. This habit of bailing immediately has some sharp corners like the aforementioned Stream implementations. To be honest, i'm not totally sure the best way around it from a library design perspective, but i also haven't worked on egg-mode in months, so i bet there's something i'm missing.

I have been using this crate as a way to learn rust, and have been quite confused about how to handle rate limit errors.
It seems to me from reading this that if you want to handle the basic cases of errors, like ratelimitstatus, then streaming it will result in an error, with no way to start again from the point at which you encountered the error.

Now that some time has passed since this issue, would you say it is worth looking at this issue again? Feels like a shame to leave this hanging