Fork me on GitHub
#clojure
<
2017-06-04
>
seancorfield01:06:52

@gmercer The ideal is if start and stop are idempotent so, yes, nil them out when you stop a system. And start can check if they’re not nil and only start things that are not nil.

seancorfield01:06:27

Oh, I see what you’re suggesting now… in the context of the code that @fbielejec posted…

seancorfield01:06:33

No, the handler component doesn’t need to assoc :database database — that’s done by Component itself.

seancorfield01:06:25

And the resources in the database would be released by called stop (which happens recursively — which you can see by adding println calls in).

gmercer01:06:10

so can you explain the failing test

seancorfield01:06:14

So in a real example, the database connection would be released when stop is called on Database.

seancorfield01:06:05

The test seems incorrect to me.

seancorfield01:06:18

Or certainly does not indicate what the OP thinks it does.

seancorfield01:06:15

(fwiw, the cloned repo throws an exception for me, as written)

gmercer01:06:46

incorrect in which way - @noisesmith pointed out that you need a reference to the started system to correctly call stop - and this aligns with the examples

gmercer01:06:13

@seancorfield mkdir test/app && mv test/system_tests.clj test/app/

seancorfield01:06:22

If you add println calls (in a do around each assoc call), you see

lein test app.system-tests
start the database
start the handlers
stop the handlers
stop the database
Which shows that any actual resources do no leak: they get cleaned up.

gmercer01:06:46

I can see the stop gets called on Database - just wondering if it the component maker's responsibility to null out references to dependencies

seancorfield01:06:08

Also, if you add an atom in the database file so you can verify the connection is actually correct inside handler, you see that Component recreates the dependencies.

seancorfield01:06:37

If you started and stopped a component and then held onto it “forever”, you’d keep a reference to a stale database in the handler (even tho’ its actual connection resource had already been cleaned up). But that’s not how things work in the real world.

seancorfield01:06:09

Normally, when you stop a Component, you either throw away the whole thing or you call start which recreates the dependencies.

seancorfield01:06:59

If you instrument the Database start and stop functions fully, you’ll see

lein test app.system-tests
start the database with connection#1
start the handlers, database is: connection#1
stop the handlers
stop the database, releasing connection#1
start the database with connection#2
start the handlers, database is: connection#2
stop the handlers
stop the database, releasing connection#2

seancorfield01:06:39

Here you see that Component has started the database and bound it into the handler by the time it calls start on the handler — by that point, the old database dependency is no longer referenced: no leak even of the memory.

gmercer01:06:47

I understand that it is cleaned up - I guess @fbielejec was asking why it doesn't look like it was cleaned up

fbielejec20:06:55

gmercer: Correct, I just don't think that the parental component should be responsible for cleaning the dependecy reference, even if it is just for the aethetic, so to say reaons. In the example I moved the namespace to a correct directory, thn you for pointing that out.

gmercer01:06:18

https://cb.codes/a-tutorial-of-stuart-sierras-component-for-clojure/ the defrecord Worker definitely clears out references to dependencies if @fbielejec was to follow this pattern the confusion would subside, yes?

gmercer01:06:19

actually that example seems to not correctly deal with the stop-chan 😢

hlolli09:06:00

No lisp here https://github.com/tensorflow but haskell and rust 😞

liminal1810:06:31

Just to make sure if I use r/map on a vector it will run in paralell? Which structures default to non-concurrency evaluation?

liminal1810:06:40

Same for pmap

Alex Miller (Clojure team)13:06:53

@liminal18 only r/fold works in parallel on maps and vectors. pmap will do chunked parallel execution over sequential data.

liminal1813:06:02

@alexmiller ok thanks so what struts does r/map do parallel execute on?

Alex Miller (Clojure team)13:06:11

r/map doesn't do anything by itself. You need to use it with r/fold to get parallel execution.

Alex Miller (Clojure team)13:06:31

And I have longer explanation in Clojure Applied

saicheong14:06:32

Is there a Clojure library for producing plain text report - for example Household Pet Inventory, Page 1 ID NAME DIET OUNCES ------------------------------------------------- 12 Buster Tuna 1.00 1 Mimi Pounces 0.30 37 Ginger Fancy Feast 0.75 19 Ellie White Castle 9.99 The columns are aligned, decimal points may be aligned etc

blueberry14:06:43

@saicheong The function that does just what you need is right there in your clojure installation. cl-format

pesterhazy14:06:19

@blueberry could you explain how you can use cl-format to build a table?

blueberry14:06:59

For the one in the example, I'd map the input sequence of data with a cl formatter function, which can finely format and align the columns.

blueberry14:06:25

and the OP did not even request a table just a plain text report that has nice alignment.

metametadata15:06:20

Out of curiosity, is there any way to execute one-off tasks via nREPL? E.g. to migrate the db after deploying the webapp:

lein repl :connect  "(app.db/migrate)"

noisesmith15:06:17

you can host nrepl in your app, but it's easier to use clojure's built in socket server

noisesmith15:06:41

that's just an arg to your java command

-Dclojure.server.repl="{:port 5555 :accept clojure.core.server/repl}"

noisesmith15:06:08

avoid using lein to run an app in production - it's a build tool

metametadata15:06:52

Right. But assuming the server is already running in webapp (at port 7888), is there then a way to execute a Clojure form via CLI once?

pesterhazy15:06:39

wouldn't this work? echo "(app.db/migrate)" | lein repl :connect

noisesmith15:06:15

@metametadata yes, if it starts a repl host, and I'm recommending using a raw socket server (built into clojure) instead of using lein for the client and server

noisesmith15:06:42

but pesterhazy's answer works if you are determined to use lein in production

pesterhazy15:06:06

I think @metametadata wants to work with an existing server, not adapt a solution where you have control of the server

metametadata15:06:48

well, I'm ambivalent about the specific client, it can be lein repl, boot or something else 🙂

metametadata15:06:23

so yeah, let's say I just want to migrate the db after deploying the app

metametadata15:06:10

piping actually works!

metametadata15:06:57

but it doesn't seem to return the error exit code if there was an exception

noisesmith15:06:24

@metametadata don't use lein on production, but if you do at least address the points here, it's a lot simpler to use an uberjar and there are far fewer gotchas https://github.com/technomancy/leiningen/blob/master/doc/TUTORIAL.md#server-side-projects

pesterhazy15:06:38

agree with noisesmeith that the socket server is a much better solution - you can just use netcat then

metametadata15:06:03

my the theoretical plan is to deploy an uberjar, and then use nREPL to perform one-off tasks, like migrations

metametadata15:06:42

I don't think it counts as using lein on production

noisesmith15:06:48

OK - just making sure

noisesmith15:06:10

in that case the socket server, built into clojure, is a lot easier to do than running nrepl inside your own app and using lein to connect to it

noisesmith15:06:48

I used to start an nrepl server, before the socket repl option existed

metametadata15:06:41

Probably yes, I can use the command-line option to start the server in uberjar instead of hardcoding it in application. But there's still a question on how to easily execute tasks via CLI without interactively running an REPL session 🙂 @pesterhazy provided one of the solutions.

noisesmith15:06:08

netcat does this with the socket repl

noisesmith15:06:48

echo "(db/migrate)" | nc localhost 5555

metametadata15:06:05

ah, got it, I'm going to try this

metametadata15:06:47

I missed that there's a difference between socket and nREPL protocols

noisesmith15:06:07

yeah, socket repl is just bytes in bytes out

noisesmith15:06:30

$ echo "(println \"hello\")" | nc localhost 5555
user=> hello
nil

noisesmith15:06:47

I bet it would even show you a stack trace if you had an error...

noisesmith15:06:12

$ echo "(/ 1 0)" | nc localhost 5555
user=> ArithmeticException Divide by zero  clojure.lang.Numbers.divide (Numbers.java:158)
user=>$

metametadata15:06:00

Yup, this works, thank you! Though it also doesn't return the exit code:

ᐅ echo "(/ 1 0)" | nc localhost 5555
user=> ArithmeticException Divide by zero  clojure.lang.Numbers.divide (Numbers.java:158)
user=> %
ᐅ echo $?                           
0

metametadata16:06:53

And if I do want to sometimes control the server via interactive REPL then I suppose nREPL is more user-friendly? E.g. I can see that it supports autocompletions when used via lein repl :connect.

noisesmith16:06:04

yeah - but clojure doesn't have exit codes, but it does print return values

noisesmith16:06:39

@metametadata only if you load the right nrepl middleware to support it, but yes

noisesmith16:06:06

I find rlwrap plus a socket repl suffices, and it reduces the complexity of my code and the size of my jar to leave nrepl out

metametadata16:06:50

Got it! Yes, I added (clojure.tools.nrepl.server/start-server :port 7888 :bind "localhost") into the sample project here.

noisesmith16:06:30

yeah, but that won't give you completion - you need middlware set up for that

metametadata16:06:37

hmm, I doubt I added the middleware manually

metametadata16:06:08

if it's not in the start-server can it be the feature of lein then?

noisesmith16:06:23

lein is setting up your nrepl middleware

noisesmith16:06:58

I mean - maybe you are getting completion out of the box? it wasn't so simple when I last checked...

metametadata16:06:05

I dunno, I think I will have to re-read the docs 🙂 My impression was that middleware is added on the "server-side".

noisesmith16:06:15

but your project still has to set up the middleware, unless the completion middleware is installed by default

metametadata16:06:23

okay, then it looks like start-server is all I added

metametadata16:06:28

@noisesmith after digging in sources it looks to me like autocompletion is the feature of lein repl and it's implemented in https://github.com/trptcolin/reply

noisesmith16:06:07

right, but it's done via middleware - you can add reply to your app

noisesmith16:06:13

that's what I was trying to say

noisesmith16:06:21

(not knowing the precise package adding it...)

metametadata16:06:37

aah, okay 🙂

metametadata16:06:03

and in that case it would also work with say rlwrap nc

noisesmith16:06:44

well, for interactive I'd use telnet instead of nc

noisesmith16:06:51

but it relies on the nrepl protocol

noisesmith16:06:57

so it's not quite that simple

noisesmith16:06:25

nrepl is more complex than you might think, it abstracts messages and replies with data structures

qqq16:06:33

is there any book / tutorial on "deploying clojure on aws" ? I find all sorts of docs on clojure web dev, but none talk about deploying to aws. Then, I find all forms of docs on AWS ec2/beanstalk/rds/.... and getting stuff to play together, but they'rea ll ruby on rails / php / python ... and not clojure.

noisesmith16:06:39

which is something things like reply rely on

noisesmith16:06:03

@qqq simplest is to make an uberjar, ensure that java is installed, and run it

qqq16:06:17

yeah that's great for webserver

noisesmith16:06:20

you don't need any other deps or setup to run it

qqq16:06:21

but then, how do you connect it to RDS ?

noisesmith16:06:47

RDS is a database, you give it your database config - or at least that's how I do it

qqq16:06:00

so I have a RDS postgresql instance; I have a elastic beanstalk server running, but it's not clear how to get the two to talk to each other

noisesmith16:06:31

OK you really don't need elasticbeanstalk but if you wnat that you don't want an uberjar you want an uberwar

noisesmith16:06:38

and you have to mess with the servlet configs

noisesmith16:06:42

uberjar is easier

qqq16:06:51

I have uberjar on beanstalk

noisesmith16:06:55

all that said, you tell your clojure app the credentials to the rds db

qqq16:06:59

beansatl ksupports both tomcat war and java jars

noisesmith16:06:08

oh, it's changed since I used it then

qqq16:06:33

but you're right; maybe it'll be eaiser if I don't use beanstal at all

qqq16:06:36

and just use ec2 + rds

noisesmith16:06:45

anyway, RDS is just a db, you give your app connection config like you would with any other

qqq16:06:45

it'll be easier to see how stuff fits togehter without all the magic

noisesmith16:06:03

there might be something fancier, but I haven't needed to try it

qqq16:06:41

@noisesmith : that would be complecting it 🙂

kwladyka20:06:04

What is the different between send and send-off for agents? I know theory send is for actions CPU limited and send-off for potentially block on IO. But how exactly this functions are different? I see it is about clojure.lang.Agent/pooledExecutor vs clojure.lang.Agent/soloExecutor.

noisesmith20:06:33

@kwladyka the executors are different, the one used by send-off is an expandable pool, the other isn't

noisesmith20:06:48

if your work is CPU bound, expanding the pool of threads to do it in slows you down

noisesmith20:06:07

if it's IO bound, it's good to be able to expand the pool

kwladyka20:06:08

so pooledExecutor is like agents can use only X at once (like async go), but soloExecutor is independent?

kwladyka20:06:08

like async/go vs async/thread ?

noisesmith20:06:01

yes, just like that

kwladyka20:06:09

great, thank you!

john23:06:46

@kwladyka In the doc, you said "so send use limited pool by CPU for agents to not overload CPU"

john23:06:23

IMO, it's more like you want to use send when you want to fully saturate your cores with work.

john23:06:39

@noisesmith that's a little bit risky, though, right? You'd want to ensure an attacker can't cause you to spawn 10 million threads.

john23:06:50

With the auto expanding thread pool

noisesmith23:06:22

right, it's not a good idea to let requests map 1:1 with your code period

noisesmith23:06:34

there should be backpressure before anything touching agents

john23:06:07

There you go using big words again 😛

john23:06:13

So, what you're saying is, I should be managing my send-offs behind a queue either way, right?

john23:06:56

Which I can rate limit as necessary