@oahner ^:on-reload :noop
should only care about namespace recompilations / state redefinition. start / stop should work as usual.
can you give an example?
let's see
it's the update-meta!
in mount-it
; it always runs even with :noop
so the state gets overwritten with the blank state in @meta-state
I had assumed it would behave like a defonce but no; the derefable state is defined with defonce but the content always gets reset by the defstate call
What I wanted out of :noop is a defonce-like defstate that does absolutely nothing on a subsequent call and keeps the initial state running as-is
While it does keep the initial state running through a reload (a webserver that binds to port 80 in the :start fn will not stop), it still replaces the state's @meta-state entry with a new, not running state, so the next (stop) does nothing and the subsequent (start) fails to start the actual webserver since port 80 is still in use by the previous state
yep, I see it. it does not cleanup the second time, since it "thinks" the state is stopped:
boot.user=> (require '[mount.tools.graph :as g])
nil
boot.user=> (pprint (g/states-with-deps))
({:name "#'boot.user/beatles",
:order 1,
:status #{:stopped},
:deps #{}})
exactly 🙂
wonder why I have not noticed it before..
since I frequently have http server as a reload noop
I guess the kicker is in the second call to start
which usually does not happen, since it's a noop
to begin with: i.e. the same state keeps running
well, the second start
sees a stopped state since mount-it
replaces the entry with a blank one
this is an interesting corner case: you redefined a noop
state with a noop
the only trace of it is in @mount/running
right you can:
boot.user=> (mount/running-states)
#{"#'boot.user/beatles"}
not to get down to atoms
I've been poking around 😉
that's very good 🙂
so let's replay this scenario from the way you work with the real namespace
you have something like:
(defstate ^{:on-reload :noop} http-server
:start (start-www (config :hubble))
:stop ((:stop-server http-server)))
defined
then you recompile the ns
http-server
keeps running
correct, but after the recompile, @meta-state's #'http-server entry now points to a new var
let me reproduce it with: https://github.com/tolitius/stater/tree/master/smsio
one sec
as an aside, if :on-reload :noop really was a noop on subsequent calls, there is a possibility that the initial running service (in this case http-server) could break with clojure.tools.namespace's refresh
, since it undefs pretty much everything, even a defonce, but I'm not sure it actually deletes anything from memory, so the thread spawned by start-www
probably wouldn't get affected
java.net.BindException: Address already in use
java.lang.RuntimeException: could not start [#'app.www/web-server] due to
hmm.. I wonder what "broke?" it. looks like a noop
state should not be marked as :stopped
that's the behavior I get alright
also weird that it just said "due to" and stops there
I get that too
mount would still remember states in t.n.refresh
you can do *e
yep, it sure does, thx to disable-reload!
to see the exception
I suppose t.n.refresh
can't entirely unmap the existing web-service since a) mount still has a reference to it and b) the whole environment gets captured by the closure that calls start-www
, but I'm pulling facts out of thin air on this subject
I sure am glad I'm not the only one getting this behavior tho, I spent the entire day looking at examples and other projects to see where I was using mount wrong
ok, so https://github.com/tolitius/mount/blob/master/src/mount/core.cljc#L150 should not be called (when (and ((running-states) s-name) (= :noop on-reload)))
I am sure something has introduced this.. I wonder what it was
:reload
stuff is really hard to test in cljs
it might be the cljs
direction that pushed a bad commit to clj
side
that sounds like a good explanation
it must be rather recent too, this is a pretty standard setup
yea..
so now I wonder whether a :noop
state could/should be ever redefined..
i.e. one thing is to recompile a namespace
another thing is to manually redefine a state
well, a t.n.refresh
doesn't break a running web server, even tho everything under it gets undef'd
the lesser recompiles, either (require ... :reload)
and just copy/pasting the defstate
block into the repl, don't actually undef vars defined with defonce either, so they're pretty safe
meanwhile, when running a webserver in debug mode, most projects I've seen refer to the handler by ref so it always points to the latest defined routes anyway
I think a :noop
should only be lifecycled through an explicit start
/`stop` call
right, that is how it was designed to work 🙂
but.. how do you redefined that state if you need to?
I suppose swap-states
could be made to be the recommended way to do such a thing
OR
make it so a :noop state can only be redefined if it's not running
but that's less explicit
what I've been doing so far is comment out the on-reload meta on the defstate, running it, then uncommenting it with the modified defstate again
but that's clearly inferior to the alternatives
I am looking at it.. the fix is not hard. the tricky part is to make "macro + var + ns" work in both clj
and cljs
since cljs
does not really have var
s and ns
🙂
yeah, that's why I still use a good old ctrl-r for cljs ^^
figwheel makes development painless enough
I guess I have not noticed it lately since iff I change the ns
where I have things like server(s) defined, I want the server to restart..
that's probably it, I haven't seen :noop used much outside of the luminus template
yea, I love these challenges.. only when you think "ok, this is finally rock solid" people who pay attention have a question 🙂
heck, for now I think I'll just put the web-server into its own namespace without making it :noop and disable-reload! it
that should do the trick
why do you have it :noop
to being with: what do you change in that namespace that does not require a server restart?
ah, see this is where my scenario is unique
I have everything in core.clj ^^'
it's a brand new project, haven't started splitting it off into multiple files yet
nobody in their right mind would do that ,right?
ah.. ok
s/cljs/clj
this is a fun bug tho
I learned more about how clojure deals with namespaces in the last 24 hours than in the 2 years before
I guarantee you there is even more to learn in that direction 🙂
yeeeah, quite the rabbit hole
i.e. the next 2% https://gist.github.com/tolitius/c17b2d4a98eabb591926
those times when I thought I'll just use a bootstrapped cljs to deal with ns api..
it was a couple of years ago, I was too naive back then
oh wait, I was wrong
t.n.refresh
does break the initial state with :noop
well, I mean, it still works, it'll just never pick up any further changes
can you try a new 0.1.12-SNAPSHOT
?
with your previous (i.e. initial) setup
sure thing
taking a while, had to nuke my .m2 repo
strange.. I can pull it, and see it here: https://clojars.org/mount/versions/0.1.12-SNAPSHOT
weird, my local copy wouldn't update itself
even after you delete it?
oh yes that always works
it's fine now
that actually happens to me quite often with SNAPSHOT releases
I like the alphaN convention for that
beautiful!
works flawlessly
many, many thanks!
well, for clj at least
yea, I am testing for cljs
.. it is a bit more involved 🙂
so now ring's wrap-reload works without restarting the state, and t.n.refresh :after 'reset
works by restarting the state
just perfect
I can't think of a nicer REPL environment, got all the bases covered
great 🙂 do you need to refresh it often?
I usually have something like: https://github.com/tolitius/stater/blob/master/smsio/dev/dev.clj
where I might add a mount/stop-except
to (stop)
and mostly use (reset)
not unless I break something in my REPL really
right, ok
for the most part ring-reload does everything
but having t.n.refresh is helpful for those edge cases where the state has just diverged too badly
ohh, didn't know about mount-up
interesting
yea, I used to carry this everywhere: https://github.com/tolitius/mount/blob/master/dev/clj/app/utils/logging.clj
ohh, I love cprop btw, I had a small utils module in my projects that pretty much did exactly that but worse and with less features, was an easy swap
but then I had spare 30 min "to decouple"..
ah, thanks 🙂
yea, cprop
also started as a traveler file from project to project, until "I got my self together"..
hehe
I mostly use it with envoy
the last couple of years: https://github.com/tolitius/envoy
i.e. merging overrides from consul
i.e. https://github.com/tolitius/envoy#merging-configurations
holy crap
when I land to places with no consul, I start my own 🙂
that's neat!
and then slowly convince people that it is a good idea
then I add a sprinkle of vault https://www.dotkam.com/2017/01/10/hubble-space-mission-securely-configured/ and I am a happy camper
a centralized server for all your projects' configuration and credentials that auto updates to your clojure config at runtime? what's not to like 🙂
right.. "auto updates to your clojure config at runtime" may not be to everyone's liking (ops people don't find this too appealing), but merging overrides (and secrets from vault) on start instead of carrying different files / ENV for different environments is a big win
exactly
tested ":noop" in cljs
. visually works
can also redefine it after it is stopped
cljs.user=> (defstate ^{:on-reload :noop} web-server :start 28 :stop :stopped)
#'cljs.user/web-server
cljs.user=> (mount/start)
{:started ["#'cljs.user/web-server"]}
cljs.user=> @@#'cljs.user/web-server
28
cljs.user=> (defstate ^{:on-reload :noop} web-server :start 28 :stop :stopped)
#'cljs.user/web-server
cljs.user=> @@#'cljs.user/web-server
28
cljs.user=> (mount/stop)
{:stopped ["#'cljs.user/web-server"]}
cljs.user=> (defstate ^{:on-reload :noop} web-server :start 42 :stop :stopped)
#'cljs.user/web-server
cljs.user=> (mount/start)
{:started ["#'cljs.user/web-server"]}
cljs.user=> @@#'cljs.user/web-server
42
nice!
oh, so a noop state gets redefined if it's stopped?
yes
perfect
I thought it should be
makes sense to me
so I clicked and clicked and clicked from those last links you shared and now I'm looking at hazelcast
how I never heard of it before is beyond me, but damn, that looks nice
yea, I love it
am I reading correctly that this is on par with redis?
especially if you are coming from JVM, it is really great
well, yeah, pretty much
I've been fighting with my IT guys to get redis and so far it's been blocked because "not supported on Windows Server" well enough and we're a microsoft shop
but this looks like it might fit the bill
hazelcast is quite robust. redis is definitely a lot more known
but "hz" guys are really active
and it has lots of solid features
well that and I skimmed through the readme on https://github.com/tolitius/chazel
damn
chazel
only implements features I actively use. it is minor compare to what hazelcast
can do
only
lol
I am thinking on adding "Continuous Query Cache"
they had it in the paid version, but now it is open sourced
well, either way, there goes my weekend
haha, yea, good luck 🙂
I am sure you'll get a kick out of hazelcast
, it is really simple, straight forward and quite powerful
problems start when you have large heaps
i.e. over 70, 80 gigs
they have an "off heap" storage as well, but it is paid
but usage with a reasonable heap is quite solid
heh, if we ever have a system at work that requires so much heap, it'll be a nice problem to have
the problems I deal with aren't exactly web scale or big data
once you have memory to put things in, you put more every day 🙂
true ^^