guix-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

System deployment commands hoarding all RAM


From: Sergio Pastor Pérez
Subject: System deployment commands hoarding all RAM
Date: Sun, 26 May 2024 14:44:52 +0200

Hello all.

Michal and I have been experimenting with a recursive function to apply
procedures to all elements that match a certain type within a
collection.

With this new procedure one could write something like this:
--8<---------------cut here---------------start------------->8---
(apply-to-record-type
  (lambda (_)
    (@ (gnu packages games) cowsay))
  (@ (guix packages) <package>)
  my-operating-system)
--8<---------------cut here---------------end--------------->8---

Which will replace every package on the system for the cowsay
package. Albeit a cowsified OS will not be very useful, other kinds of
transformations could greatly simplify OS definitions. It would make it
very convenient to deploy systems with grafts on important libraries
that are part of many packages. A more useful example would be this
syntax rule:
--8<---------------cut here---------------start------------->8---
(define-syntax-rule (custom-libc-operating-system exp ...)
  "Like 'operating-system' but graft 'libc' with the a custom 'libc'
package."
  (apply-to-record-type
    replace-libc
    (@ (guix packages) <package>)
    (operating-system exp ...)))
--8<---------------cut here---------------end--------------->8---

Which, if `replace-libc` grafts your custom libc using
`package-input-rewriting`, would allow you to define a system like so:
--8<---------------cut here---------------start------------->8---
(custom-libc-operating-system
  ...)
--8<---------------cut here---------------end--------------->8---

Instead of having to manually apply the function to every package in the
system which is very cumbersome to do for packages within services.

The function that provides this convenience is this one:
--8<---------------cut here---------------start------------->8---
(define (apply-to-record-type fn type var)
  "Recursing into child fields, apply FN to every element of VAR which holds a
value of TYPE."
  (let ((type-predicate (record-predicate type)))
    (cond ((type-predicate var)
           (fn var))
          ((procedure? var)
           (lambda args
             (apply values
                    (map
                     (lambda (var)
                       (apply-to-record-type fn type var))
                     (call-with-values
                         (lambda () (apply var args))
                       list)))))
          ((list? var)
           (map (lambda (elt)
                  (apply-to-record-type fn type elt))
                var))
          ((vector? var)
           (vector-map (lambda (vec elt)
                         (apply-to-record-type fn type elt))
                       var))
          ((and (record? var)
                (not (eq? (@ (gnu services) <service-type>)
                          (record-type-descriptor var))))
           (let* ((record-type (record-type-descriptor var))
                  (record-fields (record-type-fields record-type)))
             (apply
              (record-constructor record-type)
              (map (lambda (field)
                     (let* ((accessor (record-accessor record-type field))
                            (val (accessor var)))
                       (apply-to-record-type fn type val)))
                   record-fields))))
          (else
           var))))
--8<---------------cut here---------------end--------------->8---

During our testing, we have observed that it successfully applies the
function to every element of the record and it's child. Also, innocuous
transformations output the same system derivation. For example:
--8<---------------cut here---------------start------------->8---
(apply-to-record-type
   (lambda (var)
     var)
   (@ (guix packages) <package>)
   my-operating-system)
--8<---------------cut here---------------end--------------->8---

Should return the same OS applied or not, since it is not doing
anything. And, as expected, we see:
--8<---------------cut here---------------start------------->8---
λ guix system build sheepbook.scm
/gnu/store/kpk9la4h9xwp6n7vabd5lfs6kbhb2f2d-system

λ guix system build transformed-sheepbook.scm
/gnu/store/kpk9la4h9xwp6n7vabd5lfs6kbhb2f2d-system
--8<---------------cut here---------------end--------------->8---

All this is well and good but we have noticed this issue when trying to
deploy the system. Issuing the `reconfigure` or `vm` subcomands hoards
all the RAM of the system until the OS kills the process. This is the
output when trying to build the images:
--8<---------------cut here---------------start------------->8---
λ guix system vm sheepbook.scm
/gnu/store/c3pv8hwckbl01qacdpckn9yfwr74k629-run-vm.sh

λ guix system vm transformed-sheepbook.scm 
Killed
--8<---------------cut here---------------end--------------->8---

Monitoring the RAM usage of the second process, one can see that it will
take all the RAM available. Given that both system definitions produce
the same output, I would expect the deployment process to be identical.

Is this a bug on how Guix deploys the system? Or the transformation we
are applying introduces a cycle somewhere that only affects the
deployment commands?

Thanks for your time. Have a nice evening.
Sergio.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]