diff options
| -rw-r--r-- | config-daemon.ac | 11 | ||||
| -rw-r--r-- | guix/store/deduplication.scm | 69 | ||||
| -rw-r--r-- | nix/libstore/gc.cc | 4 | ||||
| -rw-r--r-- | nix/libstore/local-store.hh | 3 | ||||
| -rw-r--r-- | nix/libstore/optimise-store.cc | 15 | ||||
| -rw-r--r-- | tests/derivations.scm | 14 | ||||
| -rw-r--r-- | tests/nar.scm | 7 | ||||
| -rw-r--r-- | tests/store-deduplication.scm | 41 | ||||
| -rw-r--r-- | tests/store.scm | 4 |
9 files changed, 126 insertions, 42 deletions
diff --git a/config-daemon.ac b/config-daemon.ac index 5ddc740600c..86306effe1f 100644 --- a/config-daemon.ac +++ b/config-daemon.ac | |||
| @@ -94,17 +94,6 @@ if test "x$guix_build_daemon" = "xyes"; then | |||
| 94 | AC_CHECK_FUNCS([lutimes lchown posix_fallocate sched_setaffinity \ | 94 | AC_CHECK_FUNCS([lutimes lchown posix_fallocate sched_setaffinity \ |
| 95 | statvfs nanosleep strsignal statx]) | 95 | statvfs nanosleep strsignal statx]) |
| 96 | 96 | ||
| 97 | dnl Check whether the store optimiser can optimise symlinks. | ||
| 98 | AC_MSG_CHECKING([whether it is possible to create a link to a symlink]) | ||
| 99 | ln -s bla tmp_link | ||
| 100 | if ln tmp_link tmp_link2 2> /dev/null; then | ||
| 101 | AC_MSG_RESULT(yes) | ||
| 102 | AC_DEFINE(CAN_LINK_SYMLINK, 1, [Whether link() works on symlinks.]) | ||
| 103 | else | ||
| 104 | AC_MSG_RESULT(no) | ||
| 105 | fi | ||
| 106 | rm -f tmp_link tmp_link2 | ||
| 107 | |||
| 108 | dnl Check for <locale>. | 97 | dnl Check for <locale>. |
| 109 | AC_LANG_PUSH(C++) | 98 | AC_LANG_PUSH(C++) |
| 110 | AC_CHECK_HEADERS([locale]) | 99 | AC_CHECK_HEADERS([locale]) |
diff --git a/guix/store/deduplication.scm b/guix/store/deduplication.scm index cd9660174cf..370df4a74cf 100644 --- a/guix/store/deduplication.scm +++ b/guix/store/deduplication.scm | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | ;;; GNU Guix --- Functional package management for GNU | 1 | ;;; GNU Guix --- Functional package management for GNU |
| 2 | ;;; Copyright © 2017 Caleb Ristvedt <caleb.ristvedt@cune.org> | 2 | ;;; Copyright © 2017 Caleb Ristvedt <caleb.ristvedt@cune.org> |
| 3 | ;;; Copyright © 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org> | 3 | ;;; Copyright © 2018-2021 Ludovic Courtès <ludo@gnu.org> |
| 4 | ;;; | 4 | ;;; |
| 5 | ;;; This file is part of GNU Guix. | 5 | ;;; This file is part of GNU Guix. |
| 6 | ;;; | 6 | ;;; |
| @@ -22,12 +22,13 @@ | |||
| 22 | 22 | ||
| 23 | (define-module (guix store deduplication) | 23 | (define-module (guix store deduplication) |
| 24 | #:use-module (gcrypt hash) | 24 | #:use-module (gcrypt hash) |
| 25 | #:use-module (guix build utils) | 25 | #:use-module ((guix build utils) #:hide (dump-port)) |
| 26 | #:use-module (guix build syscalls) | 26 | #:use-module (guix build syscalls) |
| 27 | #:use-module (guix base32) | 27 | #:use-module (guix base32) |
| 28 | #:use-module (srfi srfi-11) | 28 | #:use-module (srfi srfi-11) |
| 29 | #:use-module (srfi srfi-34) | 29 | #:use-module (srfi srfi-34) |
| 30 | #:use-module (srfi srfi-35) | 30 | #:use-module (srfi srfi-35) |
| 31 | #:use-module (rnrs bytevectors) | ||
| 31 | #:use-module (rnrs io ports) | 32 | #:use-module (rnrs io ports) |
| 32 | #:use-module (ice-9 ftw) | 33 | #:use-module (ice-9 ftw) |
| 33 | #:use-module (ice-9 match) | 34 | #:use-module (ice-9 match) |
| @@ -37,6 +38,31 @@ | |||
| 37 | dump-file/deduplicate | 38 | dump-file/deduplicate |
| 38 | copy-file/deduplicate)) | 39 | copy-file/deduplicate)) |
| 39 | 40 | ||
| 41 | ;; TODO: Remove once 'dump-port' in (guix build utils) has an optional 'len' | ||
| 42 | ;; parameter. | ||
| 43 | (define* (dump-port in out | ||
| 44 | #:optional len | ||
| 45 | #:key (buffer-size 16384)) | ||
| 46 | "Read LEN bytes from IN (or as much as possible if LEN is #f) and write it | ||
| 47 | to OUT, using chunks of BUFFER-SIZE bytes." | ||
| 48 | (define buffer | ||
| 49 | (make-bytevector buffer-size)) | ||
| 50 | |||
| 51 | (let loop ((total 0) | ||
| 52 | (bytes (get-bytevector-n! in buffer 0 | ||
| 53 | (if len | ||
| 54 | (min len buffer-size) | ||
| 55 | buffer-size)))) | ||
| 56 | (or (eof-object? bytes) | ||
| 57 | (and len (= total len)) | ||
| 58 | (let ((total (+ total bytes))) | ||
| 59 | (put-bytevector out buffer 0 bytes) | ||
| 60 | (loop total | ||
| 61 | (get-bytevector-n! in buffer 0 | ||
| 62 | (if len | ||
| 63 | (min (- len total) buffer-size) | ||
| 64 | buffer-size))))))) | ||
| 65 | |||
| 40 | (define (nar-sha256 file) | 66 | (define (nar-sha256 file) |
| 41 | "Gives the sha256 hash of a file and the size of the file in nar form." | 67 | "Gives the sha256 hash of a file and the size of the file in nar form." |
| 42 | (let-values (((port get-hash) (open-sha256-port))) | 68 | (let-values (((port get-hash) (open-sha256-port))) |
| @@ -127,11 +153,27 @@ Note: TARGET, TO-REPLACE, and SWAP-DIRECTORY must be on the same file system." | |||
| 127 | (unless (= EMLINK (system-error-errno args)) | 153 | (unless (= EMLINK (system-error-errno args)) |
| 128 | (apply throw args))))))) | 154 | (apply throw args))))))) |
| 129 | 155 | ||
| 156 | (define %deduplication-minimum-size | ||
| 157 | ;; Size below which files are not deduplicated. This avoids adding too many | ||
| 158 | ;; entries to '.links', which would slow down 'removeUnusedLinks' while | ||
| 159 | ;; saving little space. Keep in sync with optimize-store.cc. | ||
| 160 | 8192) | ||
| 161 | |||
| 130 | (define* (deduplicate path hash #:key (store (%store-directory))) | 162 | (define* (deduplicate path hash #:key (store (%store-directory))) |
| 131 | "Check if a store item with sha256 hash HASH already exists. If so, | 163 | "Check if a store item with sha256 hash HASH already exists. If so, |
| 132 | replace PATH with a hardlink to the already-existing one. If not, register | 164 | replace PATH with a hardlink to the already-existing one. If not, register |
| 133 | PATH so that future duplicates can hardlink to it. PATH is assumed to be | 165 | PATH so that future duplicates can hardlink to it. PATH is assumed to be |
| 134 | under STORE." | 166 | under STORE." |
| 167 | ;; Lightweight promises. | ||
| 168 | (define-syntax-rule (delay exp) | ||
| 169 | (let ((value #f)) | ||
| 170 | (lambda () | ||
| 171 | (unless value | ||
| 172 | (set! value exp)) | ||
| 173 | value))) | ||
| 174 | (define-syntax-rule (force promise) | ||
| 175 | (promise)) | ||
| 176 | |||
| 135 | (define links-directory | 177 | (define links-directory |
| 136 | (string-append store "/.links")) | 178 | (string-append store "/.links")) |
| 137 | 179 | ||
| @@ -144,13 +186,18 @@ under STORE." | |||
| 144 | ((file . properties) | 186 | ((file . properties) |
| 145 | (unless (member file '("." "..")) | 187 | (unless (member file '("." "..")) |
| 146 | (let* ((file (string-append path "/" file)) | 188 | (let* ((file (string-append path "/" file)) |
| 189 | (st (delay (lstat file))) | ||
| 147 | (type (match (assoc-ref properties 'type) | 190 | (type (match (assoc-ref properties 'type) |
| 148 | ((or 'unknown #f) | 191 | ((or 'unknown #f) |
| 149 | (stat:type (lstat file))) | 192 | (stat:type (force st))) |
| 150 | (type type)))) | 193 | (type type)))) |
| 151 | (loop file type | 194 | (when (or (eq? 'directory type) |
| 152 | (and (not (eq? 'directory type)) | 195 | (and (eq? 'regular type) |
| 153 | (nar-sha256 file))))))) | 196 | (>= (stat:size (force st)) |
| 197 | %deduplication-minimum-size))) | ||
| 198 | (loop file type | ||
| 199 | (and (not (eq? 'directory type)) | ||
| 200 | (nar-sha256 file)))))))) | ||
| 154 | (scandir* path)) | 201 | (scandir* path)) |
| 155 | (let ((link-file (string-append links-directory "/" | 202 | (let ((link-file (string-append links-directory "/" |
| 156 | (bytevector->nix-base32-string hash)))) | 203 | (bytevector->nix-base32-string hash)))) |
| @@ -222,9 +269,9 @@ OUTPUT as it goes." | |||
| 222 | 269 | ||
| 223 | This procedure is suitable as a #:dump-file argument to 'restore-file'. When | 270 | This procedure is suitable as a #:dump-file argument to 'restore-file'. When |
| 224 | used that way, it deduplicates files on the fly as they are restored, thereby | 271 | used that way, it deduplicates files on the fly as they are restored, thereby |
| 225 | removing the need to a deduplication pass that would re-read all the files | 272 | removing the need for a deduplication pass that would re-read all the files |
| 226 | down the road." | 273 | down the road." |
| 227 | (define hash | 274 | (define (dump-and-compute-hash) |
| 228 | (call-with-output-file file | 275 | (call-with-output-file file |
| 229 | (lambda (output) | 276 | (lambda (output) |
| 230 | (let-values (((hash-port get-hash) | 277 | (let-values (((hash-port get-hash) |
| @@ -236,7 +283,11 @@ down the road." | |||
| 236 | (close-port hash-port) | 283 | (close-port hash-port) |
| 237 | (get-hash))))) | 284 | (get-hash))))) |
| 238 | 285 | ||
| 239 | (deduplicate file hash #:store store)) | 286 | (if (>= size %deduplication-minimum-size) |
| 287 | (deduplicate file (dump-and-compute-hash) #:store store) | ||
| 288 | (call-with-output-file file | ||
| 289 | (lambda (output) | ||
| 290 | (dump-port input output size))))) | ||
| 240 | 291 | ||
| 241 | (define* (copy-file/deduplicate source target | 292 | (define* (copy-file/deduplicate source target |
| 242 | #:key (store (%store-directory))) | 293 | #:key (store (%store-directory))) |
diff --git a/nix/libstore/gc.cc b/nix/libstore/gc.cc index e1d07651543..16519116e4f 100644 --- a/nix/libstore/gc.cc +++ b/nix/libstore/gc.cc | |||
| @@ -606,7 +606,9 @@ void LocalStore::removeUnusedLinks(const GCState & state) | |||
| 606 | throw SysError(format("statting `%1%'") % path); | 606 | throw SysError(format("statting `%1%'") % path); |
| 607 | #endif | 607 | #endif |
| 608 | 608 | ||
| 609 | if (st.st_nlink != 1) { | 609 | /* Drop links for files smaller than 'deduplicationMinSize', even if |
| 610 | they have more than one hard link. */ | ||
| 611 | if (st.st_nlink != 1 && st.st_size >= deduplicationMinSize) { | ||
| 610 | actualSize += st.st_size; | 612 | actualSize += st.st_size; |
| 611 | unsharedSize += (st.st_nlink - 1) * st.st_size; | 613 | unsharedSize += (st.st_nlink - 1) * st.st_size; |
| 612 | continue; | 614 | continue; |
diff --git a/nix/libstore/local-store.hh b/nix/libstore/local-store.hh index 9ba37219da0..20d3c3c8930 100644 --- a/nix/libstore/local-store.hh +++ b/nix/libstore/local-store.hh | |||
| @@ -292,4 +292,7 @@ void canonicaliseTimestampAndPermissions(const Path & path); | |||
| 292 | 292 | ||
| 293 | MakeError(PathInUse, Error); | 293 | MakeError(PathInUse, Error); |
| 294 | 294 | ||
| 295 | /* Size below which a file is not considered for deduplication. */ | ||
| 296 | extern const size_t deduplicationMinSize; | ||
| 297 | |||
| 295 | } | 298 | } |
diff --git a/nix/libstore/optimise-store.cc b/nix/libstore/optimise-store.cc index eb303ab4c35..9fd6f3cb356 100644 --- a/nix/libstore/optimise-store.cc +++ b/nix/libstore/optimise-store.cc | |||
| @@ -15,6 +15,9 @@ | |||
| 15 | 15 | ||
| 16 | namespace nix { | 16 | namespace nix { |
| 17 | 17 | ||
| 18 | /* Any file smaller than this is not considered for deduplication. | ||
| 19 | Keep in sync with (guix store deduplication). */ | ||
| 20 | const size_t deduplicationMinSize = 8192; | ||
| 18 | 21 | ||
| 19 | static void makeWritable(const Path & path) | 22 | static void makeWritable(const Path & path) |
| 20 | { | 23 | { |
| @@ -105,12 +108,12 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHa | |||
| 105 | return; | 108 | return; |
| 106 | } | 109 | } |
| 107 | 110 | ||
| 108 | /* We can hard link regular files and maybe symlinks. */ | 111 | /* We can hard link regular files (and maybe symlinks), but do that only |
| 109 | if (!S_ISREG(st.st_mode) | 112 | for files larger than some threshold. This avoids adding too many |
| 110 | #if CAN_LINK_SYMLINK | 113 | entries to '.links', which would slow down 'removeUnusedLinks' while |
| 111 | && !S_ISLNK(st.st_mode) | 114 | saving little space. */ |
| 112 | #endif | 115 | if (!S_ISREG(st.st_mode) || ((size_t) st.st_size) < deduplicationMinSize) |
| 113 | ) return; | 116 | return; |
| 114 | 117 | ||
| 115 | /* Sometimes SNAFUs can cause files in the store to be | 118 | /* Sometimes SNAFUs can cause files in the store to be |
| 116 | modified, in particular when running programs as root under | 119 | modified, in particular when running programs as root under |
diff --git a/tests/derivations.scm b/tests/derivations.scm index cd165d1be6a..0775719ea35 100644 --- a/tests/derivations.scm +++ b/tests/derivations.scm | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | ;;; GNU Guix --- Functional package management for GNU | 1 | ;;; GNU Guix --- Functional package management for GNU |
| 2 | ;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org> | 2 | ;;; Copyright © 2012-2021 Ludovic Courtès <ludo@gnu.org> |
| 3 | ;;; | 3 | ;;; |
| 4 | ;;; This file is part of GNU Guix. | 4 | ;;; This file is part of GNU Guix. |
| 5 | ;;; | 5 | ;;; |
| @@ -170,11 +170,15 @@ | |||
| 170 | #f)))) | 170 | #f)))) |
| 171 | 171 | ||
| 172 | (test-assert "identical files are deduplicated" | 172 | (test-assert "identical files are deduplicated" |
| 173 | (let* ((build1 (add-text-to-store %store "one.sh" | 173 | ;; Note: DATA must be longer than %DEDUPLICATION-MINIMUM-SIZE. |
| 174 | "echo hello, world > \"$out\"\n" | 174 | (let* ((data (make-string 9000 #\a)) |
| 175 | (build1 (add-text-to-store %store "one.sh" | ||
| 176 | (string-append "echo -n " data | ||
| 177 | " > \"$out\"\n") | ||
| 175 | '())) | 178 | '())) |
| 176 | (build2 (add-text-to-store %store "two.sh" | 179 | (build2 (add-text-to-store %store "two.sh" |
| 177 | "# Hey!\necho hello, world > \"$out\"\n" | 180 | (string-append "# Hey!\necho -n " |
| 181 | data " > \"$out\"\n") | ||
| 178 | '())) | 182 | '())) |
| 179 | (drv1 (derivation %store "foo" | 183 | (drv1 (derivation %store "foo" |
| 180 | %bash `(,build1) | 184 | %bash `(,build1) |
| @@ -187,7 +191,7 @@ | |||
| 187 | (file2 (derivation->output-path drv2))) | 191 | (file2 (derivation->output-path drv2))) |
| 188 | (and (valid-path? %store file1) (valid-path? %store file2) | 192 | (and (valid-path? %store file1) (valid-path? %store file2) |
| 189 | (string=? (call-with-input-file file1 get-string-all) | 193 | (string=? (call-with-input-file file1 get-string-all) |
| 190 | "hello, world\n") | 194 | data) |
| 191 | (= (stat:ino (lstat file1)) | 195 | (= (stat:ino (lstat file1)) |
| 192 | (stat:ino (lstat file2)))))))) | 196 | (stat:ino (lstat file2)))))))) |
| 193 | 197 | ||
diff --git a/tests/nar.scm b/tests/nar.scm index ba4881caaa1..98752f20884 100644 --- a/tests/nar.scm +++ b/tests/nar.scm | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | ;;; GNU Guix --- Functional package management for GNU | 1 | ;;; GNU Guix --- Functional package management for GNU |
| 2 | ;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org> | 2 | ;;; Copyright © 2012-2021 Ludovic Courtès <ludo@gnu.org> |
| 3 | ;;; | 3 | ;;; |
| 4 | ;;; This file is part of GNU Guix. | 4 | ;;; This file is part of GNU Guix. |
| 5 | ;;; | 5 | ;;; |
| @@ -486,8 +486,9 @@ | |||
| 486 | ;; their mtime and permissions were not reset. Ensure that this bug is | 486 | ;; their mtime and permissions were not reset. Ensure that this bug is |
| 487 | ;; gone. | 487 | ;; gone. |
| 488 | (with-store store | 488 | (with-store store |
| 489 | (let* ((text1 (random-text)) | 489 | ;; Note: TEXT1 and TEXT2 must be longer than %DEDUPLICATION-MINIMUM-SIZE. |
| 490 | (text2 (random-text)) | 490 | (let* ((text1 (string-concatenate (make-list 200 (random-text)))) |
| 491 | (text2 (string-concatenate (make-list 200 (random-text)))) | ||
| 491 | (tree `("tree" directory | 492 | (tree `("tree" directory |
| 492 | ("a" regular (data ,text1)) | 493 | ("a" regular (data ,text1)) |
| 493 | ("b" directory | 494 | ("b" directory |
diff --git a/tests/store-deduplication.scm b/tests/store-deduplication.scm index b1c2d93bbd3..2950fbc1a3e 100644 --- a/tests/store-deduplication.scm +++ b/tests/store-deduplication.scm | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | ;;; GNU Guix --- Functional package management for GNU | 1 | ;;; GNU Guix --- Functional package management for GNU |
| 2 | ;;; Copyright © 2018, 2020 Ludovic Courtès <ludo@gnu.org> | 2 | ;;; Copyright © 2018, 2020-2021 Ludovic Courtès <ludo@gnu.org> |
| 3 | ;;; | 3 | ;;; |
| 4 | ;;; This file is part of GNU Guix. | 4 | ;;; This file is part of GNU Guix. |
| 5 | ;;; | 5 | ;;; |
| @@ -30,13 +30,40 @@ | |||
| 30 | 30 | ||
| 31 | (test-begin "store-deduplication") | 31 | (test-begin "store-deduplication") |
| 32 | 32 | ||
| 33 | (test-equal "deduplicate, below %deduplication-minimum-size" | ||
| 34 | (list #t (make-list 5 1)) | ||
| 35 | |||
| 36 | (call-with-temporary-directory | ||
| 37 | (lambda (store) | ||
| 38 | ;; Note: DATA must be longer than %DEDUPLICATION-MINIMUM-SIZE. | ||
| 39 | (let ((data "Hello, world!") | ||
| 40 | (identical (map (lambda (n) | ||
| 41 | (string-append store "/" (number->string n) | ||
| 42 | "/a/b/c")) | ||
| 43 | (iota 5)))) | ||
| 44 | (for-each (lambda (file) | ||
| 45 | (mkdir-p (dirname file)) | ||
| 46 | (call-with-output-file file | ||
| 47 | (lambda (port) | ||
| 48 | (put-bytevector port (string->utf8 data))))) | ||
| 49 | identical) | ||
| 50 | |||
| 51 | (deduplicate store (nar-sha256 store) #:store store) | ||
| 52 | |||
| 53 | ;; (system (string-append "ls -lRia " store)) | ||
| 54 | (list (= (length (delete-duplicates | ||
| 55 | (map (compose stat:ino stat) identical))) | ||
| 56 | (length identical)) | ||
| 57 | (map (compose stat:nlink stat) identical)))))) | ||
| 58 | |||
| 33 | (test-equal "deduplicate" | 59 | (test-equal "deduplicate" |
| 34 | (cons* #t #f ;inode comparisons | 60 | (cons* #t #f ;inode comparisons |
| 35 | 2 (make-list 5 6)) ;'nlink' values | 61 | 2 (make-list 5 6)) ;'nlink' values |
| 36 | 62 | ||
| 37 | (call-with-temporary-directory | 63 | (call-with-temporary-directory |
| 38 | (lambda (store) | 64 | (lambda (store) |
| 39 | (let ((data (string->utf8 "Hello, world!")) | 65 | ;; Note: DATA must be longer than %DEDUPLICATION-MINIMUM-SIZE. |
| 66 | (let ((data (string-concatenate (make-list 1000 "Hello, world!"))) | ||
| 40 | (identical (map (lambda (n) | 67 | (identical (map (lambda (n) |
| 41 | (string-append store "/" (number->string n) | 68 | (string-append store "/" (number->string n) |
| 42 | "/a/b/c")) | 69 | "/a/b/c")) |
| @@ -46,7 +73,7 @@ | |||
| 46 | (mkdir-p (dirname file)) | 73 | (mkdir-p (dirname file)) |
| 47 | (call-with-output-file file | 74 | (call-with-output-file file |
| 48 | (lambda (port) | 75 | (lambda (port) |
| 49 | (put-bytevector port data)))) | 76 | (put-bytevector port (string->utf8 data))))) |
| 50 | identical) | 77 | identical) |
| 51 | ;; Make the parent of IDENTICAL read-only. This should not prevent | 78 | ;; Make the parent of IDENTICAL read-only. This should not prevent |
| 52 | ;; deduplication from inserting its hard link. | 79 | ;; deduplication from inserting its hard link. |
| @@ -54,7 +81,7 @@ | |||
| 54 | 81 | ||
| 55 | (call-with-output-file unique | 82 | (call-with-output-file unique |
| 56 | (lambda (port) | 83 | (lambda (port) |
| 57 | (put-bytevector port (string->utf8 "This is unique.")))) | 84 | (put-bytevector port (string->utf8 (string-reverse data))))) |
| 58 | 85 | ||
| 59 | (deduplicate store (nar-sha256 store) #:store store) | 86 | (deduplicate store (nar-sha256 store) #:store store) |
| 60 | 87 | ||
| @@ -77,8 +104,10 @@ | |||
| 77 | (lambda (store) | 104 | (lambda (store) |
| 78 | (let ((true-link link) | 105 | (let ((true-link link) |
| 79 | (links 0) | 106 | (links 0) |
| 80 | (data1 (string->utf8 "Hello, world!")) | 107 | (data1 (string->utf8 |
| 81 | (data2 (string->utf8 "Hi, world!")) | 108 | (string-concatenate (make-list 1000 "Hello, world!")))) |
| 109 | (data2 (string->utf8 | ||
| 110 | (string-concatenate (make-list 1000 "Hi, world!")))) | ||
| 82 | (identical (map (lambda (n) | 111 | (identical (map (lambda (n) |
| 83 | (string-append store "/" (number->string n) | 112 | (string-append store "/" (number->string n) |
| 84 | "/a/b/c")) | 113 | "/a/b/c")) |
diff --git a/tests/store.scm b/tests/store.scm index 2150a0048cb..5c9f651d6c0 100644 --- a/tests/store.scm +++ b/tests/store.scm | |||
| @@ -759,7 +759,9 @@ | |||
| 759 | 759 | ||
| 760 | (test-assert "substitute, deduplication" | 760 | (test-assert "substitute, deduplication" |
| 761 | (with-store s | 761 | (with-store s |
| 762 | (let* ((c (random-text)) ; contents of the output | 762 | ;; Note: C must be longer than %DEDUPLICATION-MINIMUM-SIZE. |
| 763 | (let* ((c (string-concatenate | ||
| 764 | (make-list 200 (random-text)))) ; contents of the output | ||
| 763 | (g (package-derivation s %bootstrap-guile)) | 765 | (g (package-derivation s %bootstrap-guile)) |
| 764 | (d1 (build-expression->derivation s "substitute-me" | 766 | (d1 (build-expression->derivation s "substitute-me" |
| 765 | `(begin ,c (exit 1)) | 767 | `(begin ,c (exit 1)) |
