Repository navigation
Deal with github bridge import rate limit - #585
Conversation
|
Let me tell you, I like this very much. Ping me when you think it's ready. |
|
Yes and thanks |
|
Testing on nixpkgs! It's trudging along, but shows no signs of stopping due to ratelimit, or errors, so all good so far :) I think I will attempt to bridge pull all of nixpkgs's issues |
This comment has been minimized.
This comment has been minimized.
|
So, mine got much further, but also failed, at this point: No clue why yours happened though |
|
https://docs.github.com/en/graphql/overview/resource-limitations says there is a per hour ratelimit so maybe I should just wait one hour then. I still wonder why ctrl-c didn't work though |
|
@rng-dynamics note I did try this with an earlier commit. If you've fixed the issue already, I'm happy to rebuild and retry |
|
@mohe2015 as I retry with the same code currently, it does alert me loudly that it is "SLEEPING FOR 1855 SECONDS" |
|
Yeah, just read the code (don't know go though) and found out it's supposed to print something. But for me it just hangs. |
|
I did a clean btw |
This comment has been minimized.
This comment has been minimized.
|
Can I somehow clean all git-bug's data for debugging? |
|
Presumably deleting all refs/bugs, then a git gc (aggressive) |
|
For me this happens reproducibly so maybe you could also try with the latest commit: |
|
New error: |
GC is not necessary. There is some target in the makefile to automate cleaning: |
|
I would like to kindly ask you for a few more days. There are two bugs in the current code: one is a deadlock in certain situations (which surfaced by refactoring) and one has to do with empty titles of some github issues. Kindly note that this is a Draft PR and is still work in progress. |
| break | ||
| } | ||
| issueCursor = issues.PageInfo.EndCursor | ||
| for _, issueNode := range issues.Nodes { |
There was a problem hiding this comment.
I might be wrong but when you have a loop pushing to a channel like that, you might need to wrap it a select that also monitor the context, so that the loop can exit properly when needed.
Something like that:
for _, foo := range bar {
// do something with foo
foo2 := foo + "hello"
select {
case <-ctx.Done():
return
case out<-foo2:
}
}There was a problem hiding this comment.
This might explain why the import doesn't stop properly like in #585 (comment)
There was a problem hiding this comment.
Yes, you are right, I included it in one of my commits.
|
Suggestion to find the races (and possibly deadlock):
Deadlock are trickier but the above might give you some hints. Otherwise, maybe https://github.com/sasha-s/go-deadlock ? |
|
@rng-dynamics Sure, no problem. It's our fault if we try too eagerly. But thanks so we now know that it's expected to not work yet. |
Of course. No expectations, just wanted to help test :) |
The old implementation of the github bridge used maps to store several channels holding data obtained from the Github API. Removing the maps and simply packing data and channels together in a struct and passing it through one single channel makes the program simpler in terms of concurrency and, additionally, enables the garbage collector to free the memory gradually without any additional provisions.
|
Importing the issues from https://github.com/MichaelMure/git-bug I get: Importing from https://github.com/datalad/datalad: Importing from https://github.com/NixOS/nixpkgs: |
|
I still have to change the way the rate limit is communicated to the user. |
|
@rng-dynamics iotop, kde system monitor and iostat report a disk write of ~150M/s by git-bug, is that realistic? I'm testing with https://github.com/NixOS/nixpkgs. Storage use seems to grow with ~0.5M/s (measured by eyes on `df --block-size=1M) |
|
@mohe2015: Good question; On my system I measure ~50MB/s with iostat. The difference in disk usage in the .git folder is 1.835GB (comparing before and after importing all the issues of https://github.com/NixOS/nixpkgs). The import takes 457 minutes. That is, the actual occupied disk space grows only with an average of ~67KB per second. I do not fully know how the data model of git-bug works. I can only speculate about the cause for these disk writes: Having a short peek at the document describing the data model makes me wonder if we are overwriting a lot of files. The bridge adds one item after another to git-bug. But operations in the git-bug data model are gathered into OperationPacks (which are saved in git blobs). The data model also mentions git rebase, which could also be a cause for changing files. Well, ... I might be wrong. 🤔 |
|
@mohe2015 so you have a 1.1GB index for the full text search ... this is not good :-| ... but nothing to do with this PR. |
|
@rng-dynamics if you think this branch is better than what is in Also, if you rebase your branch, the two issues you faced should be fixed now. |
@MichaelMure: You are right, we should try to merge it. It's for sure much better than what is in |
MichaelMure
left a comment
There was a problem hiding this comment.
This is great, but the rate-limiting feedback should be reworked to let the UIs choose how to present the information to the users.
| func (gi *githubImporter) Init(_ context.Context, repo *cache.RepoCache, conf core.Configuration) error { | ||
| func (gi *githubImporter) Init(_ context.Context, _ *cache.RepoCache, conf core.Configuration) error { | ||
| gi.conf = conf | ||
| return nil |
There was a problem hiding this comment.
So you moved the credential loading + client creation in ImportAll. Not what I imagined but I guess that make sense.
My tiny annoyance with that though is that this has now a different behavior than the other bridges. Could you make the same change there as well?
There was a problem hiding this comment.
My original intention was to simplify the code. Looking at it now makes me also wonder if it really makes any significant difference. The difference in the local code seems to be: When doing the credential loading + client creation in ImportAll, then we do not have to save a *githubv4.Client variable in the githubImporter struct (importMediator already holds the *githubv4.Client), and there is no risk in having two non-identical *cache.RepoCache parameters in the calls to Init and ImportAll.
I could undo this change and move the credential loading + client creation back into Init if that's what you prefer. Please let me know again what you prefer: (1) move credential loading + client creation back into Init, or (2) keeping it in ImportAll and make the same change in all the other bridges.
There was a problem hiding this comment.
As you said there is not much difference so both is fine to me as long as it's consistent. (2) has the nice property of avoiding the extra value in the struct, so let's do that one?
There was a problem hiding this comment.
I did that with 3d14e2e for the Gitlab and Jira bridges. There was no need to change anything in the Launchpad bridge.
| var nextEvent ImportEvent | ||
| var err error | ||
| for { | ||
| // We need the current event and one look ahead event. |
There was a problem hiding this comment.
nitpicking: it'd be great if you could explain why this structure with look ahead is necesary
There was a problem hiding this comment.
Before I add the comment to the code I would like to ask you for additional input: The reason for using a look ahead element is that in case a github issue is followed by a github issue edit, then in the call to NewBugRaw we use both, the github issue and the first github issue edit, to create the bug instance. This behaviour originates from the previous versions of the github bridge and I adopted it. It seems that this is due to the way github serves its issues on the API. (The same is true for comments.) My question is the following: Is this behaviour necessary? Would it be okay to simply create a (possibly empty) bug instance and then apply the first github issue edit in order to create the issue/bug content. This would cause the edit-history of a github issue and a corresponding git-bug bug instance to not be equal (one might display without "edit" and the other might display with "edit"). I assume that would not be acceptable - right?
There was a problem hiding this comment.
Isn't that because on github it's sort of as if the initial creation of an issue is (creation+comment), while in git-bug it's the same operation? I don't remember exactly.
I assume that would not be acceptable - right?
At least not desirable. But your code does that properly at the moment, no?
Just a thought, but there might be a way to simplify. Instead of maintaining two two values currEvent and nextEvent:
- simply iterate on the event stream
- when you know you need an extra event, consume an extra one
There was a problem hiding this comment.
Isn't that because on github it's sort of as if the initial creation of an issue is (creation+comment), while in git-bug it's the same operation? I don't remember exactly.
In github the issue holds the most recent content. Because we want to imitate the history, we sometimes create the bug instance from the plain github issue (if there are no edits in github) and sometimes we create the bug from the github issue together with its first edit (if there are edits in github).
But that's also the reason why we do not know whether or not we need the next event before we looked at it.
I assume that would not be acceptable - right?
At least not desirable. But your code does that properly at the moment, no?
Yes, it is handled properly.
If you want to get rid of the look ahead, then I think it would be best to make importMediator provide more context and add a field (say hasEdits bool) to IssueEvent. Then we just know whether or not there are edits and we do not have to look ahead. (And we would have to add a similar data field to TimelineEvent, because it holds comments which might be followed by comment edits. It's the same situation.)
For now I only added a comment to the code with ede5e21 explaining why look ahead is necessary. Let me know what you think.
| event := gi.mediator.NextImportEvent() | ||
| // consume (and use) all message events | ||
| if e, ok := event.(MessageEvent); ok { | ||
| fmt.Println(e.msg) |
There was a problem hiding this comment.
The bridge itself should not write anything, this should be left to do by the various UI (CLI, termui, webui). Instead, there is this out channel where you write some core.ImportResult (might not be the best naming).
It looks like you have a need for an arbitrary-ish user feedback for the rate limiting? In that case we should define either 1) an event for an arbitrary message ImportEventMessage or 2) an event for the rate limiting ImportEventRateLimiting. The second one makes probably more sense as it would allow the UIs to special case the handling.
| for i := 0; i < retries; i++ { | ||
| // wait a few seconds before retry | ||
| sleepTime := time.Duration(8*(i+1)) * time.Second | ||
| timer := time.NewTimer(sleepTime) |
There was a problem hiding this comment.
no big deal, but FYI there is a simpler way: https://stackoverflow.com/a/62287775/6594169
There was a problem hiding this comment.
I looked at the docs which made me reflect on the following: With time.NewTimer it is possible to cancel/stop the timer in case it is not needed anymore (e.g., in case the context.Done() channel is closed). That's not possible with the time.After call. The effective difference is that the memory of the timer will be freed only after the timer is really expired. Here that is not a big difference because the goroutine is sleeping only a few seconds. But a bit further down when we are handling the rate limiting, then the goroutine may be sleeping for up to an hour and canceling/stoping the timer might be favorable:
https://github.com/MichaelMure/git-bug/blob/b2e98ef07f3083db303099fbcedbd98a33ad1164/bridge/github/import_mediator.go#L413
| @@ -0,0 +1,436 @@ | |||
| package github | |||
There was a problem hiding this comment.
This is GREAT. Nice work to have come up with this structure.
Gitlab and Jira bridge: move credential loading and client creation from `Init` to `ImportAll` in order to harmonize the behaviour of the different bridges.
The Github bridge itself should not write anything. This commit removes code writing to stdout and itroduces an event `ImportEventRateLimiting` to `core.ImportResult` in order to inform about a rate limiting situation of the Github GraphQL API. Now the communication with the user is delegated to the various user interfaces.
|
I fixed the merge conflict and did some minor cleanup. Most of those are insignificant but there was an unhandled error : 10a80f1#diff-3ae542ca4dfb02e8b746658def040e984685424467c3e158d7494768e55fa90dR571-R573 If all if fine for you, we can merge 🎉 🎉 ! |
|
Actually no, the gitlab bridge seems to be broken now |
|
I will have to do some manual testing to see what is the cause. (Not all tests are running on my machine due to missing API tokens. Also the CI did not run the tests before you approved them.) |
|
It's not only the gitlab bridge but also the github bridge. I don't really understand why I didn't see that error for the github bridge in the CI in b2e98ef. |
This reverts commit 3d14e2e.
Reason: failing integration tests
|
Yeah I don't get it either why this failure happened. It would require some debugging but well it's working now so let's move on and merge. Great work ! :-) |
|
Thanks for merging it! |
|
You are both wonderful people, thanks for the work! |
An attempt to solve #315.
Changes: