Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Missing tweets in the timeline. #147

Closed

Description

@j1warren

I can't prove it with a specific example, obviously, but I notice it a 2nd time already.

Two days ago I've closed cawbird, and only now remembered, that it was closed.
So I open it, and scroll back till I find the last tweets I've seen and then some more.
And I notice some earlier tweets that I have not seen the last time I've had cawbird open.

I'm not 100% sure, so I'd like to ask people to take a look if they can see it, too.

PS It might be cawbird or it may be the twitter api, or something else.

Activity

  1. IBBoard commented on May 1, 2020

    @IBBoard
    Owner

    I wondered whether I'd seen this a few times, but I think it's technically out of our control. This may get a little technical. TL;DR: We're not doing anything wrong, it's just the way Twitter works, but we might be able to work around it and do better.

    Because of its size, Twitter's database is "eventually consistent". Twitter specifically said once:

    Availability: Most of our use cases strongly favor availability over consistency, so an always-on eventually consistent database was a must.

    What that means is that if someone posts a tweet from Japan and then you query from the UK, it's entirely possible that the server that you're asking for updates doesn't yet know about the new tweet. Someone querying from Japan would get the new tweet, though, because they'd ask a server that knew it.

    By the time you close and re-open Cawbird then Twitter's database has eventually become consistent (for that tweet) and so it appears where it was missed before.

    Technically, we're doing everything correct. We're retrieving new tweets from the timeline since the last tweet we saw. Which is fine. But it might mean that there are occasions when we pull tweet every tweet up to X, then a new one appears at X-1†, but our next request is "give me everything after X" and so we miss X-1.

    What I've considered (but never done) is to adjust the "everything since" parameter and push it back a little bit to give us some overlap to get the "eventually consistent" tweets that appear later. The problem is that you're then balancing "are we doing enough overlap to catch everything?" (we don't know exactly how long "eventually" takes) with "are we wasting bandwidth pulling too many duplicate tweets?".

    † IDs are a combination of timestamp, the worker that accepted the tweet, and a sequence number to avoid duplication (src)

  2. added
    enhancementNew feature or request
    externalProblems caused by dependencies and other systems
    on May 1, 2020
  3. j1warren commented on May 1, 2020

    @j1warren
    Author

    Maybe create a configurable parameter for overlap and when displaying those 'lost' tweets highlight them and log the sufficient overlap in logs for these lost&found tweets.
    So after time we could know what overlap covers 100% cases.

  4. IBBoard commented on May 1, 2020

    @IBBoard
    Owner

    Yeah, the first approach would definitely be to put in a bigger overlap than I think is necessary and then log what gets missed while I ran it locally for a while (and pushed the code here for others to use, if they could rebuild their own).

    There might always be a "long tail" to the distribution, though. One second might cover 95%, 15 seconds covers 99%, 60 seconds covers 99.9%, and so on. It's unlikely ("eventually consistent" is normally quite quick, because otherwise the database is fairly useless) but unlikely is not impossible.

    We could go back an entire hour. But we can only pull 200 tweets in one go (and we do it every two minutes) so rapidly changing feeds might miss or delay tweets because they won't fit in the 200 tweet cap!

    But as I said, there's only so much we can do.

    I guess the other question is what we do with the UI, which will partly depend on how often it occurs and how far back they go. Do we just insert them where they should be (and so you might miss them if they're below where you're currently looking) or do we put them at the top and show them slightly out of order (but we sort by tweet ID at the moment, so what do we sort on?)

  5. added a commit that references this issue on May 17, 2020
  6. IBBoard commented on May 17, 2020

    @IBBoard
    Owner

    Okay, a first cut approach with a bit of debugging is in place. I've not seen anything yet, but it is Sunday evening and people aren't posting that much.

    Note that the numbers won't just end "â€Ķ00000" because the IDs are built by ORing binary values, not the base-10 numbers.

    Current overlap is ~8s. To find the Unix timestamps of tweets do ((id>>22)/1000) + 1288834974 (because Twitter works to milliseconds and has its own epoch!)

  7. j1warren commented on May 18, 2020

    @j1warren
    Author

    Great! Compiled, will run this for a while to see, too.

  8. added 2 commits that reference this issue on May 18, 2020
  9. j1warren commented on May 19, 2020

    @j1warren
    Author

    Got some

    00:13:50.904: Loading since 1262460247444815872 (was 1262460260930990080)
    00:13:50.904: REST: GET 1.1/statuses/home_timeline.json?since_id=1262460247444815872&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    00:13:51.298: Loaded missing tweet: 1262460258548580353
    00:15:50.904: Loading since 1262461140798013440 (was 1262461154078502913)
    00:15:50.904: REST: GET 1.1/statuses/home_timeline.json?since_id=1262461140798013440&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    00:15:51.297: Loaded missing tweet: 1262461145044025344
    00:17:50.900: Loading since 1262461140798013440 (was 1262461154078502913)
    00:17:50.900: REST: GET 1.1/statuses/home_timeline.json?since_id=1262461140798013440&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    00:17:51.253: Loaded missing tweet: 1262461145044025344
    00:19:50.873: Loading since 1262462034151211008 (was 1262462052452335616)
    00:19:50.873: REST: GET 1.1/statuses/home_timeline.json?since_id=1262462034151211008&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    00:21:50.906: Loading since 1262462858784931840 (was 1262462871574896645)
    =============
    01:31:51.064: Loading since 1262480073013854208 (was 1262480092174757888)
    01:31:51.064: REST: GET 1.1/statuses/home_timeline.json?since_id=1262480073013854208&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    01:31:51.742: Loaded missing tweet: 1262480074911203328
    01:33:50.908: Loading since 1262480073013854208 (was 1262480092174757888)
    01:33:50.908: REST: GET 1.1/statuses/home_timeline.json?since_id=1262480073013854208&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    01:33:51.055: Loading since 1262480073013854208 (was 1262480092174757888)
    01:33:51.055: REST: GET 1.1/statuses/home_timeline.json?since_id=1262480073013854208&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    01:33:51.679: Loaded missing tweet: 1262480074911203328
    01:35:50.908: Loading since 1262480073013854208 (was 1262480092174757888)
    01:35:50.908: REST: GET 1.1/statuses/home_timeline.json?since_id=1262480073013854208&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    01:35:51.286: Loaded missing tweet: 1262480074911203328
    01:37:50.908: Loading since 1262480073013854208 (was 1262480092174757888)
    01:37:50.908: REST: GET 1.1/statuses/home_timeline.json?since_id=1262480073013854208&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    01:37:51.299: Loaded missing tweet: 1262480074911203328
    01:39:50.908: Loading since 1262480073013854208 (was 1262480092174757888)
    01:39:50.908: REST: GET 1.1/statuses/home_timeline.json?since_id=1262480073013854208&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    =============
    01:39:51.053: Loading since 1262480073013854208 (was 1262480092174757888)
    01:39:51.053: REST: GET 1.1/statuses/home_timeline.json?since_id=1262480073013854208&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    01:39:51.698: Loaded missing tweet: 1262480074911203328
    01:41:50.908: Loading since 1262480073013854208 (was 1262480092174757888)
    01:41:50.908: REST: GET 1.1/statuses/home_timeline.json?since_id=1262480073013854208&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    =============
    01:41:51.074: Loading since 1262480073013854208 (was 1262480092174757888)
    01:41:51.074: REST: GET 1.1/statuses/home_timeline.json?since_id=1262480073013854208&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    01:41:51.707: Loaded missing tweet: 1262480074911203328
    01:43:50.884: Loading since 1262480073013854208 (was 1262480092174757888)
    01:43:50.884: REST: GET 1.1/statuses/home_timeline.json?since_id=1262480073013854208&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    01:43:51.300: Loaded missing tweet: 1262480074911203328
    01:45:50.908: Loading since 1262480073013854208 (was 1262480092174757888)
    01:45:50.908: REST: GET 1.1/statuses/home_timeline.json?since_id=1262480073013854208&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    =============
    01:45:51.055: Loading since 1262480073013854208 (was 1262480092174757888)
    01:45:51.055: REST: GET 1.1/statuses/home_timeline.json?since_id=1262480073013854208&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    01:45:51.724: Loaded missing tweet: 1262480074911203328
    01:47:50.908: Loading since 1262480073013854208 (was 1262480092174757888)
    01:47:50.908: REST: GET 1.1/statuses/home_timeline.json?since_id=1262480073013854208&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    01:47:51.290: Loaded missing tweet: 1262480074911203328
    01:49:50.911: Loading since 1262484642859057152 (was 1262484669905252353)
    01:49:50.911: REST: GET 1.1/statuses/home_timeline.json?since_id=1262484642859057152&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    =============
    10:31:50.904: Loading since 1262616275016744960 (was 1262616306093830146)
    10:31:50.904: REST: GET 1.1/statuses/home_timeline.json?since_id=1262616275016744960&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    10:31:51.248: Loaded missing tweet: 1262616303820582912
    10:33:50.880: Loading since 1262616275016744960 (was 1262616306093830146)
    10:33:50.880: REST: GET 1.1/statuses/home_timeline.json?since_id=1262616275016744960&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    10:33:51.245: Loaded missing tweet: 1262616303820582912
    10:35:50.903: Loading since 1262616275016744960 (was 1262616306093830146)
    10:35:50.903: REST: GET 1.1/statuses/home_timeline.json?since_id=1262616275016744960&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    10:35:51.286: Loaded missing tweet: 1262616303820582912
    10:37:50.904: Loading since 1262616275016744960 (was 1262616306093830146)
    10:37:50.904: REST: GET 1.1/statuses/home_timeline.json?since_id=1262616275016744960&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    10:37:51.262: Loaded missing tweet: 1262616303820582912
    10:39:50.903: Loading since 1262616275016744960 (was 1262616306093830146)
    10:39:50.903: REST: GET 1.1/statuses/home_timeline.json?since_id=1262616275016744960&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    10:39:51.292: Loaded missing tweet: 1262616303820582912
    10:41:50.883: Loading since 1262616275016744960 (was 1262616306093830146)
    10:41:50.883: REST: GET 1.1/statuses/home_timeline.json?since_id=1262616275016744960&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    10:41:51.523: Loaded missing tweet: 1262616303820582912
    10:43:50.904: Loading since 1262616275016744960 (was 1262616306093830146)
    10:43:50.904: REST: GET 1.1/statuses/home_timeline.json?since_id=1262616275016744960&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    10:43:51.356: Loaded missing tweet: 1262616303820582912
    10:45:50.904: Loading since 1262619917149011968 (was 1262619917582508038)
    10:45:50.904: REST: GET 1.1/statuses/home_timeline.json?since_id=1262619917149011968&count=200&tweet_mode=extended&include_my_retweet=true&contributor_details=true
    
  10. IBBoard commented on May 19, 2020

    @IBBoard
    Owner

    That's helpful, thanks. I need a script to pull these apart rather than doing it by hand!

    Quick check of the first one says that it's got the same timestamp (1589828955) but then the first bits of the rest of it differs. I'll have to check more, though, because I must be doing something wrong as I'm finding that the "missed" one has a higher value in the rest of the bits (which should make it later!)

  11. IBBoard commented on May 20, 2020

    @IBBoard
    Owner

    Okay, a one-liner (ish) in Bash (mostly).

    tweetid=<tweet_id_number>
    date -d @$(((${tweetid}>>22) / 1000 + 1288834974)); echo "obase=2 ; $(($tweetid & ((1<<22)-1)))" | bc
    

    Change tweetid and re-run to get the different values.

    That gives us:

    id type date data centre/worker/sequence
    1262460260930990080 last load Mon 18 May 20:09:15 BST 2020 101101011000000000000
    1262460258548580353 missed Mon 18 May 20:09:15 BST 2020 101100000000000000001
    1262461154078502913 last load Mon 18 May 20:12:48 BST 2020 101000011000000000001
    1262461145044025344 missed Mon 18 May 20:12:46 BST 2020 101010000000000000000
    1262480092174757888 last load Mon 18 May 21:28:03 BST 2020 101000011000000000000
    1262480074911203328 missed Mon 18 May 21:27:59 BST 2020 101110100000000000000
    1262616306093830146 last load Tue 19 May 06:29:19 BST 2020 101101011000000000010
    1262616303820582912 missed Tue 19 May 06:29:19 BST 2020 101111011000000000000

    The biggest gap we see there is 4 seconds (1262480092174757888 was loaded before we saw 1262480074911203328). And I've also realised that the rest of the bits in the ID aren't important, because as soon as the time is earlier then the whole ID is earlier! (obviously)

    So, given that it is only occasional tweets, they're within a few seconds of the latest tweet, and you've gone several polls without any update, I think it's probably safe to just insert them in-line (i.e. where they should be, and not at the top of the window).

  12. added a commit that references this issue on May 20, 2020
  13. IBBoard commented on May 20, 2020

    @IBBoard
    Owner

    Okay, I think that fixes it and has just enough useful logging for the final version without being a complete debug version.

    I'll try to keep an eye on it for a bit to see how much we can reduce the overlap (because one minute of overlap when we're polling every two minutes is quite a lot!). Any further examples of time gaps would be good.

  14. j1warren commented on May 21, 2020

    @j1warren
    Author

    FYI, I've ran for 2 days with those 2 commits from 3 days ago - didn't have a single missed tweet logged.
    Will recompile with 7d04c85.

  15. IBBoard commented on May 23, 2020

    @IBBoard
    Owner

    Okay, so it's definitely a rare event then. And so far we might get away with ~10 seconds (which I guess will be 8 seconds, because we're doing a binary mask)

  16. added a commit that references this issue on May 27, 2020
  17. added a commit that references this issue on Aug 22, 2020
  18. j1warren commented on Sep 30, 2020

    @j1warren
    Author

    I know there's probably nothing to be done, but I've just restarted cawbird and found some tweets that were missing from my previous long-running instance.

    Maybe new twitter API will fix this.

  19. IBBoard commented on Oct 3, 2020

    @IBBoard
    Owner

    @j1warren Can you remember what kind of a gap it was? And was it many or just a few? And from one user or many?

    We can try increasing the overlap, but the more we do that then the more often people are going to re-fetch known tweets as well.

    At the end of the day, Twitter is using an "eventually consistent" database, so nothing we do is ever going to be 100% perfect.

  20. j1warren commented on Oct 7, 2020

    @j1warren
    Author

    @IBBoard
    I scrolled till 2 days back, and it was a 2 single tweets from different people.
    I have a feeling it might the twitter itself, importing them after hour/two into the feed because if their own internal delays.

    I still haven't implemented #2, otherwise I'd tell you the exact timings.

  21. IBBoard commented on Oct 7, 2020

    @IBBoard
    Owner

    And you're certain they weren't there? Because sometimes I use my mobile, see a tweet in the middle of a batch of tweets that I was sure wasn't there, and then go back and find it was there and I just blanked it 🙂

    If you did implement #2 then you'd have to track the retrieval time as well to see where it fitted in the chronology. I'm hoping it was still a small gap, but just too long for us to pick up. Because if they are adding tweets after an hour or more then we stand no chance! (But that is technically possible for an "eventually consistent" database)

  22. IBBoard commented on Oct 13, 2020

    @IBBoard
    Owner

    And sometimes there's just nothing we can do.

    Opened Cawbird. There's a thread from Tim Bray showing tweets 1, 4, 5, 6 and 7. But it should have been in the middle of a batch, so it doesn't make sense.

    Close and re-open and now I see 1, 2, 3, 6 and 7. So even between requests the API isn't always consistent!

  23. added a commit that references this issue on Jan 20, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestexternalProblems caused by dependencies and other systems

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions