Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for lazy-loading by requiring octokit/lazy, rather than just octokit #1431

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

timrogers
Copy link
Contributor

#1351 introduced support for "lazy loading" in the library rather than loading everything upfront. @gmcgibbon ran some benchmarks and found that this approach shaved 400ms off their application startup.

This was a nice idea - however, it ended up unexpected being a breaking change because users were relying on all classes and modules in Octokit being loaded upfront (e.g. so rescue Octokit::TooManyRequests works). We ended up reverting the change in #1428.

This introduces new, opt-in lazy-loading. All you need to do to take advantage of it is require 'octokit/lazy' instead of requiring plan old 'octokit'.

In our CI run, we test the gem with both methods of requiring, which should help to avoid any unexpected surprises when we release changes.

@timrogers
Copy link
Contributor Author

@gmcgibbon How do you feel about this approach? It allows you to opt-in for faster boot times, whilst not ending up being a breaking change in certain cases.

@timrogers timrogers requested a review from nickfloyd June 7, 2022 12:19
octokit_require = ENV.fetch('OCTOKIT_REQUIRE', 'octokit')
require octokit_require

require 'faraday'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to manually require Faraday because, if we end up requiring the "lazy" version of Octokit above, then Faraday won't have been loaded when we try to look at its version later in this file.

Copy link
Contributor

@gmcgibbon gmcgibbon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that you can still ship an autoloaded version of the gem without having to opt-in, it will just require a bigger refactor of each file. If we make it so each file only defines one module (and no peer modules like the error.rb file does by defining several error classes, there should be no problem).

The issue here is that that could take some time, and this approach will work for now, but the lazy loaded version of the client in this PR has the same bugs that will eventually need to be fixed.

Since I'm not the maintainer of this gem, it is not my decision, but I would refactor the file structure and then try re-introducing autoloading when this is done.

@timrogers
Copy link
Contributor Author

timrogers commented Jun 7, 2022

Thanks for taking a look at this so quickly ❤️

You're right - it would be possible to use autoload declarations throughout, in place of the current requires, and then everything can be lazy loaded seamlessly. I believe that can even work where one file provides multiple modules - we don't necessarily have to split them.

However, even with that in mind, I'd still argue that it might be better for lazy-loading to be optional. Some users would likely prefer the "work" to happen when their app boots, rather than adding "warmup time" to their first request (as an example).

What do you think? If we do want to provide optional lazy loading, then we could merge this version and then improve the lazy loading process as a follow-up.

@gmcgibbon
Copy link
Contributor

gmcgibbon commented Jun 7, 2022

What do you think? If we do want to provide optional lazy loading, then we could merge this version and then improve the lazy loading process as a follow-up.

You could also just use Zeitwerk and support optional eager loading and autoloading like Rails does to be more flexible. However, I think it would be useful to look at benchmarks or profiles of the load time added by autoloading to a first request before deciding on anything.

As an intermediary step, we can prep the repo for autoload support by organizing the modules and files like I mentioned because Zeitwerk also requires this (as does Rails).

@timrogers
Copy link
Contributor Author

I did consider Zeitwerk, but I was a little wary of adding runtime dependencies and I wasn't sure how it worked in the gem context.

I'm happy to pause this PR, do some more digging and come back.

I do think this solution is a good start, but it might be worth skipping straight to something better.

Thank you for your feedback and for the original PR - I hope we can bring these performance improvements out soon.

@github-actions
Copy link

👋 Hey Friends, this pull request has been automatically marked as stale because it has no recent activity. It will be closed if no further activity occurs. Please add the Status: Pinned label if you feel that this issue needs to remain open/active. Thank you for your contributions and help in keeping things tidy!

@github-actions github-actions bot added Status: Stale Used by stalebot to clean house and removed Status: Stale Used by stalebot to clean house labels May 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants