Skip to content

Commit

Permalink
add github actions caching
Browse files Browse the repository at this point in the history
Summary:
X-link: facebook/sapling#1005

Speed up github builds with github actions CI caching based on the cache-key mechanism already present in getdeps.

Cached fizz windows build completed in 22% of original time,  linux build in 54% of the original time,  and mac in 72% of original time.  This saves a lot of waiting around for PR builds on OSS changes as windows signal is usually the lagging one.  Speed ups will vary across the different Meta OSS projects.

Windows benefits most from caching dependencies as we don't use system packages there at all,  linux next as its default runner is slower than mac, and then finaly mac (the strongest hardware of the default github runners).

Github allows [up to 10GB cache per repo](https://github.blog/changelog/2021-11-23-github-actions-cache-size-is-now-increased-to-10gb-per-repository/), expiring data over capacity.  You can see the size of the caches generated in the [caches view of githhub actions UX](https://github.com/ahornby/fizz/actions/caches?query=sort%3Asize-desc),  looks like our usage is pretty small so far.

More background:
Github actions caching decides its own compression format (currently it prefers zstd), but they are free to change that in future, hence no .zstd suffix or similar on the cache keys.

Github actions caches from main are used from feature branches but not vice versa, hence a PR can't pollute cache for the trunk build.  This allows us to benefit from caching on main and PRs where dependency versions match.

The final item being built, and dependencies inside the same repo as the final are not cached.  A different mechanism would be necessary for those (e.g. using a tool like ccache/sccache or caching cargo/buck2 output).  This means that when building EdenFS, sapling could not be cached (its in the same repo), but folly could be as we have a key for it based on the folly-rev.txt file.

When there is a cache hit the build step is skipped using extension of the conditionals introduced in previous diff D67839708

Reviewed By: bigfootjon

Differential Revision: D67839730

fbshipit-source-id: c384a216eb27ccd3f816e3c31b167232bda571a6
  • Loading branch information
ahornby authored and facebook-github-bot committed Jan 6, 2025
1 parent f506b36 commit 027b6e5
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 6 deletions.
56 changes: 51 additions & 5 deletions build/fbcode_builder/getdeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,16 +245,16 @@ def __init__(self, cache, loader, m):
self.loader = loader
self.cache = cache

self.cache_file_name = "-".join(
self.cache_key = "-".join(
(
m.name,
self.ctx.get("os"),
self.ctx.get("distro") or "none",
self.ctx.get("distro_vers") or "none",
self.project_hash,
"buildcache.tgz",
)
)
self.cache_file_name = self.cache_key + "-buildcache.tgz"

def is_cacheable(self):
"""We only cache third party projects"""
Expand Down Expand Up @@ -560,6 +560,7 @@ def run_project_cmd(self, args, loader, manifest):
else:
manifests = [manifest]

cache = cache_module.create_cache()
for m in manifests:
fetcher = loader.create_fetcher(m)
if isinstance(fetcher, SystemPackageFetcher):
Expand All @@ -569,6 +570,10 @@ def run_project_cmd(self, args, loader, manifest):
continue
src_dir = fetcher.get_src_dir()
print(f"{m.name}_SOURCE={src_dir}")
inst_dir = loader.get_project_install_dir_respecting_install_prefix(m)
print(f"{m.name}_INSTALL={inst_dir}")
cached_project = CachedProject(cache, loader, m)
print(f"{m.name}_CACHE_KEY={cached_project.cache_key}")

def setup_project_cmd_parser(self, parser):
parser.add_argument(
Expand Down Expand Up @@ -1235,16 +1240,50 @@ def write_job_for_platform(self, platform, args): # noqa: C901
src_dir_arg = "--src-dir=. "
has_same_repo_dep = True

out.write(" - name: Build %s\n" % m.name)
if not src_dir_arg:
# only run the step if needed
if args.use_build_cache and not src_dir_arg:
out.write(f" - name: Restore {m.name} from cache\n")
out.write(f" id: restore_{m.name}\n")
# only need to restore if would build it
out.write(
f" if: ${{{{ steps.paths.outputs.{m.name}_SOURCE }}}}\n"
)
out.write(" uses: actions/cache/restore@v4\n")
out.write(" with:\n")
out.write(
f" path: ${{{{ steps.paths.outputs.{m.name}_INSTALL }}}}\n"
)
out.write(
f" key: ${{{{ steps.paths.outputs.{m.name}_CACHE_KEY }}}}-install\n"
)

out.write(" - name: Build %s\n" % m.name)
if not src_dir_arg:
if args.use_build_cache:
out.write(
f" if: ${{{{ steps.paths.outputs.{m.name}_SOURCE && ! steps.restore_{m.name}.outputs.cache-hit }}}}\n"
)
else:
out.write(
f" if: ${{{{ steps.paths.outputs.{m.name}_SOURCE }}}}\n"
)
out.write(
f" run: {getdepscmd}{allow_sys_arg} build {build_type_arg}{src_dir_arg}{free_up_disk}--no-tests {m.name}\n"
)

if args.use_build_cache and not src_dir_arg:
out.write(f" - name: Save {m.name} to cache\n")
out.write(" uses: actions/cache/save@v4\n")
out.write(
f" if: ${{{{ steps.paths.outputs.{m.name}_SOURCE && ! steps.restore_{m.name}.outputs.cache-hit }}}}\n"
)
out.write(" with:\n")
out.write(
f" path: ${{{{ steps.paths.outputs.{m.name}_INSTALL }}}}\n"
)
out.write(
f" key: ${{{{ steps.paths.outputs.{m.name}_CACHE_KEY }}}}-install\n"
)

out.write(" - name: Build %s\n" % manifest.name)

project_prefix = ""
Expand Down Expand Up @@ -1363,6 +1402,13 @@ def setup_project_cmd_parser(self, parser):
action="store",
default=None,
)
parser.add_argument(
"--no-build-cache",
action="store_false",
default=True,
dest="use_build_cache",
help="Do not attempt to use the build cache.",
)


def get_arg_var_name(args):
Expand Down
12 changes: 11 additions & 1 deletion build/fbcode_builder/getdeps/cargo.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ def recreate_dir(self, src, dst) -> None:
shutil.rmtree(dst)
simple_copytree(src, dst)

def recreate_linked_dir(self, src, dst) -> None:
if os.path.isdir(dst):
if os.path.islink(dst):
os.remove(dst)
elif os.path.isdir(dst):
shutil.rmtree(dst)
os.symlink(src, dst)

def cargo_config_file(self):
build_source_dir = self.build_dir
if self.cargo_config_file_subdir:
Expand Down Expand Up @@ -191,7 +199,9 @@ def _build(self, reconfigure) -> None:
],
)

self.recreate_dir(build_source_dir, os.path.join(self.inst_dir, "source"))
self.recreate_linked_dir(
build_source_dir, os.path.join(self.inst_dir, "source")
)

def run_tests(self, schedule_type, owner, test_filter, retry, no_testpilot) -> None:
if test_filter:
Expand Down

0 comments on commit 027b6e5

Please sign in to comment.