From 2243838a1b5645295730e5f3f83ce71248fc55b5 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Tue, 27 Nov 2018 22:53:28 -0800 Subject: [PATCH 1/4] Make compilecache atomic Previously, the cache path is directly passed to the --output-ji option of the subprocess for generating compile cache. However, when several processes are compiling the same package, one process may modify the file while another process is calculating the checksum, yielding a broken cache. This patch let each process create a cache in a private temporary path and then atomically rename it to the final cache path. --- base/loading.jl | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index b68d005f53a8c..ce95387cce473 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1185,18 +1185,31 @@ function compilecache(pkg::PkgId, path::String) else @logmsg verbosity "Precompiling $pkg" end - p = create_expr_cache(path, cachefile, concrete_deps, pkg.uuid) - if success(p) - # append checksum to the end of the .ji file: - open(cachefile, "a+") do f - write(f, _crc32c(seekstart(f))) + # create a temporary file in `cachepath` directory, write the cache in it, + # write the checksum, _and then_ atomically swap the file with `cachefile`. + tmppath, tmpio = mktemp(cachepath) + local p + try + close(tmpio) + p = create_expr_cache(path, tmppath, concrete_deps, pkg.uuid) + if success(p) + # append checksum to the end of the .ji file: + open(tmppath, "a+") do f + write(f, _crc32c(seekstart(f))) + end + # this is atomic according to POSIX: + rename(tmppath, cachefile) + return cachefile end - elseif p.exitcode == 125 + finally + # not using `mktemp() do ...` to pass `force=true` to `rm` + rm(tmppath, force=true) + end + if p.exitcode == 125 return PrecompilableError() else error("Failed to precompile $pkg to $cachefile.") end - return cachefile end module_build_id(m::Module) = ccall(:jl_module_build_id, UInt64, (Any,), m) From d367552467ab108588b22bdfb77d8e0fd558f45c Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Wed, 28 Nov 2018 18:04:24 -0800 Subject: [PATCH 2/4] Tweak comment [no ci] --- base/loading.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/loading.jl b/base/loading.jl index ce95387cce473..aa60adf8cb4e1 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1186,7 +1186,7 @@ function compilecache(pkg::PkgId, path::String) @logmsg verbosity "Precompiling $pkg" end # create a temporary file in `cachepath` directory, write the cache in it, - # write the checksum, _and then_ atomically swap the file with `cachefile`. + # write the checksum, _and then_ atomically move the file to `cachefile`. tmppath, tmpio = mktemp(cachepath) local p try From b40b7264eeabd8c21d424354575dbfb88cc8ac8c Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Thu, 29 Nov 2018 19:50:10 -0800 Subject: [PATCH 3/4] Add NEWS and FAQ [skip ci] --- NEWS.md | 3 +++ doc/src/manual/faq.md | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/NEWS.md b/NEWS.md index 368b3fbe7e0cc..b9160111e6d27 100644 --- a/NEWS.md +++ b/NEWS.md @@ -18,6 +18,9 @@ Language changes Previously they were sometimes parsed as tuples, depending on whitespace ([#28506]). * `Regex` now behaves like a scalar when used in broadcasting ([#29913]). * `Char` now behaves like a read-only 0-dimensional array ([#29819]). + * Precompilation cache files are now created atomically ([#30174]). Note that + invoking _n_ `julia` processes simultaneously may creates _n_ temporary + copies of cache files. New library functions --------------------- diff --git a/doc/src/manual/faq.md b/doc/src/manual/faq.md index e6ae749fd1b5a..01cab373fe29f 100644 --- a/doc/src/manual/faq.md +++ b/doc/src/manual/faq.md @@ -834,6 +834,22 @@ a scalar can participate in linear algebra operations such as `2 * rand(2,2)`, but the analogous operation with a zero-dimensional array `fill(2) * rand(2,2)` is an error. +## Computing cluster + +### How do I manage precompilation cache files in distributed file systems? + +When using `julia` in high-performance computing (HPC) facilities, invoking +_n_ `julia` processes simultaneously creates at most _n_ temporary copies of +cache files. It may become a major issue in slow and/or small distributed +file systems. There are a few possible workarounds: + +1. Use `julia` with `--compilecache=no` flag to turn off precompilation. +2. Configure a private writable depot using `pushfirst!(DEPOT_PATH, private_path)` + where `private_path` is a path unique to this `julia` process. This + can also be done by setting environment variable `JULIA_DEPOT_PATH` to + `$private_path:$HOME/.julia`. +3. Create a symlink from `~/.julia/compiled` to a directory in a scratch space. + ## Julia Releases ### Do I want to use a release, beta, or nightly version of Julia? From 9844c1e36f22c60a95b89ddda9f338e665e37e0f Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Fri, 21 Jun 2019 23:56:26 -0700 Subject: [PATCH 4/4] Update NEWS.md Co-Authored-By: Charles Kawczynski --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 1090e4724e84b..0deb5ebd87f65 100644 --- a/NEWS.md +++ b/NEWS.md @@ -21,7 +21,7 @@ Language changes * Using the same name for both a local variable and a static parameter is now an error instead of a warning ([#29429]). * Precompilation cache files are now created atomically ([#30174]). Note that - invoking _n_ `julia` processes simultaneously may creates _n_ temporary + invoking _n_ `julia` processes simultaneously may create _n_ temporary copies of cache files. New library functions