4 ms·
I've sometimes used a snippet like this: (defun rebuild (&optional (sha1 (sha1-of-current-commit))) (asdf:operate 'asdf:compile-op :my-package)
by mbrock 9y ago
I've sometimes used a snippet like this:
(defun rebuild (&optional (sha1 (sha1-of-current-commit)))
(asdf:operate 'asdf:compile-op :my-package)
(asdf:operate 'asdf:load-op :my-package)
(push sha1 *sha1s-of-loaded-commits*))
(defun rebuild-if-necessary ()
(let ((sha1 (getf (current-commit) :sha1))
(loaded (car *sha1s-of-loaded-commits*)))
(unless (equal sha1 loaded)
(format t "Rebuilding since ~S differs from ~S.~%"
sha1 loaded)
(rebuild sha1)
t)))
And then in for example a web service project, I would just call that function in the basic request dispatch handler.
When a web request comes in to the Lisp system, it will check the working directory's Git repository. If the HEAD points to a new commit, it will compile the new code (to machine code, with optimizations!) and reload the whole web app package. When the REBUILD-IF-NECESSARY function call returns, the new code is already loaded, and the web request proceeds as normal, but with the new code loaded.
And yeah, the conditions system is really radically better than exceptions. Raising a condition doesn't unwind the stack, but just calls the closest condition handler, more like a function call than an exception. The handler is offered a choice of restart opportunities, so for example a FILE-NOT-FOUND condition might offer to restart with a different file path, to just retry, or to error. Having restarts available for almost all exceptional conditions makes the system much more flexible.
- Jach 9y agoWhat I can imagine freaking people out looking at your code is not knowing what, if anything, is atomic or otherwise in a transaction. Presumably that's well-defined by the ASDF operations and whatever multi-threaded solution you're using in Lisp along with the Lisp runtime guarantees, like presumably (I haven't looked) if you compile and load a package through ASDF it will do it all at once (but for each thread?) and you won't get in a state where one thread has a reference to both an old and a new function in the package. You might still have an issue where one thread expects a database schema that no longer applies because you've updated the schema and the mapping code was updated by the package reload but this thread hasn't had that happen yet... For your web system, are you guaranteeing that if any subsequent requests come in, they wait for the current request to completely finish before being processed? If so, at least there's no obvious bug and the recompile only happens once, but that will turn people away when we're in a many-CPU age. So it's really not that different from the CGI model that PHP has used so successfully. FTP a new copy of the PHP file onto the server, presto, all new requests use that code and you didn't have to restart anything.
- mbrock 9y agoOops, forgot to mention that this is a total hack that I would never put in production. I'm actually very curious about the atomicity of ASDF loads, and I don't know how it works. I wouldn't expect it to be for example atomic with respect to building several dependencies, but maybe it is. The difference from the CGI model is that the Common Lisp runtime is stateful. In this particular experiment, I was working on a phone menu system, and it was really convenient to have the state of that system as Lisp variables.