Fork me on GitHub

If possible, I try to avoid closing channels. It makes for a simpler code, IMO


Closing a channel feels a bit akin to setting the state of an object. It can be used, but make sure you really need it. Perhaps there’s a way to avoid closing the channel in the first place.


Hello 🙂 perhaps a long shot but are there any public examples of Clojure being used for a server requiring high concurrency? Like for example that of messaging applications like whatsapp (but not nearly that insane levels)?


Am thinking in most of such cases people use things like erlang when factoring downtime?


@zackteo define "high concurrency". http-kit claims to have very high throughput, as I recall.


We have a "high concurrency" messaging app at work, using nettyio but of course not public.


(I think many people think they need WhatsApp/Facebook/Twitter scale but they really don't, and it's amazing how much traffic you can handle on a pretty basic JVM-based HTTP server these days)


@seancorfield am looking at something like a customer engagement suite that will need to send for example whatsapp messages to perhaps an average of 100k people (with a higher upper limit)


I think the sending is not the issue as much dealing with the responses. Also because the from what i understand with the example of whatsapp api, the one using the api needs to handle the open connections? Like something like 1 phone number to 10k people or something


Perhaps, yeah. We send hundreds of thousands of emails a day and track deliverability and that's really no big deal (we used to send 2-3M emails a day but deliverability analysis showed that was not worthwhile -- better to send fewer, more targeted messages). So we have a dedicated process for just handling email deliverability reports -- which are posted by Perl scripts running on a PowerMTA box. Focus on stateless POSTs and avoid open HTTP connections if you can.


(a dedicated stateless process running on three servers, to clarify)


We also send a lot of web push notifications and deal with deliverability of those. Again, it's all done outside of HTTP keep-alive connections.


Actually could I ask what are the stateless POSTs?


I mean that the POST is self-contained and can go to any server in the cluster.


Each POST is "atomic".


(i.e., avoid sticky sessions etc)


Am also wondering if messaging has some difference in that the sent messages and replies need to be written to a database to track the conversation


Hmmm right, I think i still lack the knowledge and experience to understand what that might fully entail in a real world application


Our messaging server process writes everything to the DB. Uses a GraphQL API and WebSockets and NettyIO.


(and just three instances running in the cluster -- but they are fairly high memory)


I see I see! I guess I will try to do more of my own research. But do agree, think we often think the use cases need the same infrastructure of that of the tech giants like perhaps use of cassandraDB but usually that is not true


We got very enamored of MongoDB at one point because of the "web scale" hype but after a few years we migrated everything back into our "huge" Percona (MySQL) 5.7 instance and we were better off.


It's truly amazing what you can achieve with some pretty boring software and hardware.


But yeah, want to see if there a good reason to propose the use of clojure somewhere in the tech stack in such an application. But reasonably will end up using the standard like that of java, nodejs etc


Clojure wins on the development side, not on the scale/performance side, in general.

Ben Sless07:09:13

I must say wrt performance, just like most applications are not on Twitter FB scale, neither are most performance problems. You're more likely to find blocking calls on the event loop before you need to solve Clojure's performance

💯 2
Ben Sless07:09:50

And even when you do, Clojure can go much farther than most would initially guess. Yes, you might have to lie cheat and steal to make the JVM happy but it can mostly be hidden away at libraries while the business layer remains ergonomic

❤️ 2

There are some areas where the immutability is a win -- ClojureScript does better here with Reagent etc compared to plain React.js -- but mostly Clojure wins on simplicity and maintainabiity.


For us, when I introduced Clojure a decade ago it was about abstraction, first and foremost, and immutability second.


I realise also that now i have started work, some of the issues with adopting new technologies like clojure, is that it along with other jvm languages might have no proper testing analysis pipeline to go through even in a Java shop


I'm taking this to a thread at this point...


That's a cultural issue tho' (testing) not a technical one.


We can got from commit to QA in about 20-25 minutes and then to production in another 15.


but i guess those are metrics management uses to measure how well departments are doing


We're aiming to get that down to about 10 minutes (commit to QA) but the 15 minutes is a reasonable rollout across our production cluster, fully automated.


Clojure/Java doesn't really affect that.


Clojure's value prop is entirely in dev/maintenance.


Well, it eliminates an entire class of production errors (due to state), but really it's about productivity in terms of adding features.


We've measured a lot of stuff over the years, and we can get new features to production with Clojure faster than other stuff we've used in the past -- even supposedly Rapid Application Development stuff.


Language is really a very minor part of overall company efficiency. Automated testing, automated (continuous) integration, and automated (continuous) deployment have much bigger impacts on a company's ability to move nimbly forward.


We adopted Clojure a decade ago but it's really only been in the last couple of years that we've managed to implement reliable CI/CD and that has been the game-changer for overall impact.


Sure, Clojure has allowed us to make software changes much faster -- and at much bigger scale in terms of business logic -- but the real productivity boosts have come from that fast CI/CD cycle.


and guess without the CI/CD pipeline it doesn't make sense to use other languages, not to mention issues of hiring and maintenance


Yeah, to be honest, the language is irrelevant if you don't have CI/CD in place, for the most part.


And, yeah, Clojure makes some aspects of hiring harder (but also some aspects easier).


Does where you work have a fully automated testing/deployment process? A fully automated production rollout?


Does it have a formal pull request review process?


Those are fundamental.


I think they do .... i haven't been too involved with any projects at the moment to really know


But there is a fully automated production rollout from what i understand


A fully-automated CI/CD system is far more important than the language you're using.


The language is about developer pain/happiness, so it's important too -- but not as important as some of the other "basics".


Although, if a different language makes your developers a lot happier... 🙂


I've been trying to figure out where I get fulfilment out of work - having just graduated about a month ago and just started work


Yeah, that's a tricky one. I graduated university in '83, did PhD research for three years, and have worked professionally ever since, and I will say not all jobs are fulfilling and even the best job has some miserable times.


I mostly love my job right now -- ten years for the same company, 100% remote, a lot of autonomy (I introduced Scala but it didn't stick and I introduced Clojure and it did stick).


I've been very fortunate: most of my jobs have been very interesting and very challenging -- which is not true for most people in IT. I think a lot of people are a bit rose-tinted about the IT world 😞


Is quite silly that I'm already having a small existential crisis of sorts


Yeah, I don't want to be insensitive but you have decades ahead of you and you're already worrying too much 🙂


Perhaps it is also fuelled by a worry if my current role will lead me to where i want to be couple of years from now


Yeap, no offence taken 😄


I guess it is about perspective and just making best of my situation


I think career growth is orthogonal to language choice?


Right 😅 that's true hmmm

Ben Sless07:09:36

It's also not the end of the world to change direction later in life. Don't let it bother you.

👍 2

@zackteo Erlang has a built-in framework to do distributed actors, as far as I know (never used Erlang myself).


But I have worked on chat applications, in Clojure.


On the JVM, the equivalent of Erlang-style distributed actors programming would be Akka (also haven’t used it, have only read about it a bit)


Actors are a big trade-off, as far as I understand. They can achieve massive scale, but the trade-off is that any communication with actors is akin to being “locked inside a mailbox and communicating to the outside world by sending and receiving mail”


For small to medium/large scale, I would recommend using a message queue.


Amazon SQS is a nice managed option; alternatively, there’s RabbitMQ (which is actually built in Erlang itself)


I have a bit of experience with both, but it has been a while.


Thank you! 😄

👍 2

even if you use actors you'll need a queue


for almost any application the choice of implementation language is never the limiting factor, in general the limit is some dumb decision you made last year which means you have to poll for updates instead of getting them pushed to you

😂 8

(have not used this AWS service myself)


I have an endpoint that I want to serve files based on the URL params, so I’m constructing a file path using string concatenation. How can I make sure that syntax like . or .. doesn’t work? And these aren’t public files, so they don’t go into the regular public resources directory with all of the other static files.


Now I’m doing

(io/file (str files-dir fmt "/" filename))
but this is unsafe as filename can easily contain ../../other.file


It's worth mentioning that the first line of defense should be the right file permissions. Regarding dots - you can get the relative path from the URL, make it absolute, then make it canonical. Compare the canonical path to the absolute one - if they're different then something is not right.


Point taken about file permissions.


also if you're using ring you can just use a handler somebody else made and hopefully considered security


I’m using pedestal


To be more clear - just file permissions are not enough of course since the process has to be able to access its own classpath. Which, I imagine, you don't want to serve via URLs.


ring.middleware.file does the canonical path check that p-himik mentioned:

🙏 2

Yeah, like I mentioned these are not public resources


I use the resource path in pedestal for public resources like css files and so on.


@US1LTFF6D I think I’ll just include Ring as a dependency and use those functions rather than copy them


Why user given names for the files at all? Use uuid names, keeping a map of user given file names to uuids somewhere else


thanks for linking to the specific functions

👍 2

@hiredman All of the files reference each other and already use the filenames as identifiers. I would have to make a two-way mapping which seems like a pointless exercise.


I don’t control the dataset, it’s provided to me


Hmm.. actually maybe I can just use the resource path interceptor from Pedestal and put some auth interceptors in front of it.


(which incidentally reuses Ring)


Pretty simple solution

(defn ->handler
  (fn [{:keys [path-params]}]
    (let [{:keys [fmt filename]} path-params
          filepath  (str "/" fmt "/" filename)]
      (ring/file-response filepath {:root files-dir}))))


btw io/file doesn't require string concatenation:

user=> (io/file "/home" "justin")
#object[ 0x2b97cc1f "/home/justin"]


> just file permissions are not enough of course since the process has to be able to access its own classpath there's no requirement that any part of classpath be openable via, in a normal app your classpath roots should be jars, the entries of which cannot be opened via FIle


What do permissions have to do with File ? If the user that launched a Java process doesn't have an access to one of the files on the classpath, the process will fail, will it not?


Ah, I get what you mean now. But still, if /tmp/x.jar is on my classpath and my process works, won't it be able to just open /tmp/x.jar as a file?


that's true, fair point


I'm facing locally (and not in CI). Generally this functionality has worked just fine for a long time. tldr the JDK's native http client can fail opaquely saying nothing much other than unable to connect. From similar issues it looks like a SSL Cert thing. Is there anything one can do to fix certs?

Jon Boone17:09:15

Presumably you have no control over the endpoint with the bad cert?

Jon Boone17:09:17

I think there is a JVM option you can pass to have it ignore bad certs, but you should never use such a thing in production.


I don't think the cert is bad, as the GH comment says the link can be visited w/ Chrome / url


I wonder if this sort of thing would help i.e. upgrade root certs


doesn't seem to be clojure related, so maybe #clojure is not the best place for this. It seems premature to assume it is an ssl issue


I know it's not clojure related, but people with nice JVM skills hang out here. Also would serve > It seems premature to assume it is an ssl issue I made an educated guess by also looking at similar, recent issues in the same repo


the issues on the repo with the fail to download where it was concluded to be an ssl issue have stacktraces that mention ssl on them, do you?


the messages were slightly different (those included SSL, this one not), but the url being hit was the same.


so what stacktrace do you get?


it looks like the format of the error reporting is some exception the tool creates (not very useful) followed by the caused by stacktrace (very useful)


so an abbreviated version that only grabs the first part is not going to tell you anything


arch or fedora? are you using systemd-resolved?


macOS ...ultimately I can see {:type :message connect timed out i.e. the original exception and stacktrace. So their ex handling doesn't seem too terrible


anyway, that stacktrace is a failure to connect, not an ssl cert connected error at all


so a network connectivity issue, or my wild guess (which is why I asked about the os and whatnot) dns


why else would it fail on java but not curl or Chrome? And only for that particular url?


I've seen dns setups where java will fail but other tools (chrome, nslookup, ping, etc) work fine


there isn't really a single standard dns interface that is standard on linux, so different tools can end up behaving differently based on your setup, which is why I was asking about your os

👍 2

in particular I've seen that in combination when using systemd-resolved and unbound


but whatever the case, doesn't look to be ssl at all


will try out a few things, like bleeding-edge jdks thanks!


maybe try messing with the socket timeout settings



hrrm, those might not effect the httpclient


oh, they are using URLConnection, and not the cool new java http client, so those settings should work


I eyed those but the failure I'm getting is immediate. It would surprise me if something changed the default 10000 timeout to sth lower good variety of things to try anyway, thanks!


for extra context I kinda don't care about this issue, in CI it works. Most of all I was wondering if there are any "quick wins" or usually-applied fixes. For sake of adding more stuff to the thread, I also tried these for debugging. Didn't show a smoking gun unfortunately

"" ;; or:


I have this feeling that Clojure is picking up steam. Job postings everywhere and I meet a lot of people interested in the language and community. What if 2021 will be the year where Clojure is breaking to the next level of adaption? Happy Friday everyone!

clojure-spin 48
❤️ 6
💥 4
metal 4

Feels the same way to me. A few years ago I thought it would be impossible to get a clojure job. Today, I have one and so do a bunch of my friends.

metal 8
clojure-spin 6

definitely #jobs #remote-jobs feels much different than circa 2017

Ben Sless19:09:24

Happy indeed. Feels different. Less like a hype and more like "slow and steady"


Slow and steady wins the race.

🐢 10
Ben Sless19:09:57

If this is going to end up being the year of the linux desktop and the year of clojure I'm throwing a party

🎉 8
Thomas Moerman19:09:57

Cheers to that!

🍸 4

I haven't had success of getting a Clojure role yet. Perhaps because I'm geographically distant from the Clojure boom 😅 But maybe 2022/2023 will be the year 😄


Are you in China, @zackteo ? According to Calva usage the last month, Clojure is picking up at least some steam there.


@U0ETXRFEW I'm in Singapore! Was close to getting a rare Clojure role in Singapore but perhaps some lack of experience with some policies (like "non-negotiable salary arrangement") costed me the role. And had some others leads but no news soon after :x


Crossing my fingers for you landing a Clojure gig soon!

clojure-spin 4

Sweden up by 20%!? Have you been asking all your friends to download it, @U0ETXRFEW? :P


And Canada has Sweden beat!

Ben Sless12:09:18

How is that measured?


You mean the increase, @UK0810AQ2?


It’s the past four weeks compared to the four-week period before that. Total users as reported by Google Analytics.


I think I might be driving Sweden’s figures by starting Calva sessions from new Gitpod workspaces at least 10 times daily, @U4P4NREBY 😃

😁 6

The usage pattern over the weeks tells us that Clojure is very much used for business. 😀

🙃 2
Ben Sless12:09:07

Have you tried looking at trends geographically at a lower resolution? I usually find it's centered around companies which use it


I haven’t studied it very deeply at all. I just start all days by looking at that graph I just posted. 😃 It makes me happy because most often the current period is visibly above the previous one. It is super rare with that thing you can see there from last Monday, when usage was way below the previous month’s corresponding Monday. Anyone know of a holiday in Brazil last Monday that might explain. 😃


I have never looked at datafy and nav and am feeling like asking a dumb question will help me out…


(clojure.datafy/nav (clojure.datafy/datafy {:foo {:bar 1}}) :foo :bar)


gives back :bar rather than 1


I must be holding it wrong … what is the right way … with a trivial example like the above


Check @seancorfield ‘s articles on it. I don’t have a link handy


I’ve read it but there are no simple examples


I know he has done it for JDBC but it’s late and I don’t want to pore over complex code to grok something … I have assumed … is simple


the v you pass to nav there should be {:bar 1}


ok, that gives back {:bar 1} which also feels odd


nav and datafy are not for walking regular Clojure collections. They are for making it possible to walk some non-Clojure objects as if they were Clojure collections. The mentioned article by Sean Corfield: Examples and discussions: •


You see your v back because nothing extends Navigable:

(extend-protocol Navigable
  (nav [_ _ x] x))


yes, have seen it … again, examples are thin on the ground and that’s how I learn best


(let [m {:foo {:bar 1}}
      d (clojure.datafy/datafy m)
      v (get d :foo)
      m2 (clojure.datafy/nav d :foo v)
      d2 (clojure.datafy/datafy m2)
      v (get d2 :bar)]
  (clojure.datafy/nav d2 :bar v))


in :foo {:bar 1}} 1 is 2 levels deep in maps, you are trying to pull it out naving down once


which is not how it works


ah, ok … this is a nice example … thanks @hiredman


@p-himik my understanding was that nav works out of the box for maps and vectors … I just hadn’t grokked the model until now 🙂


Technically, it works for absolutely everything because of that extend-protocol on Object. It's just that it's probably not what you want for types other than Clojure's collections.


The model I try to give folks is: datafy takes you from Thing to pure data. get navigates in pure data. nav takes you from that pure data back to a different Thing and you have to give it the key and the value from the get navigate so it knows how/where to go.


Thing -> datafy -> data
               /    |
          nav      get
      /             |
Thing'             data'


Shouldn't Thing' be derived from data' instead of data?


No, you nav from data using key/value from the get to a new Thing


But it isn't necessarily true that Thing' would datafy back to data' in all cases.


A lot of times, nav implementations ignore the value (i.e., they ignore data').


In other words, when you call datafy on Thing, and it produces data, it's common to have the implementation of nav attached as metadata.


Because nav usually has to know about Thing and it may use either the key and/or value to determine what new Thing to navigate you from the original Thing.

Fredrik22:09:02 is a really nice example of all the things @seancorfield mentions above.

❤️ 2

It's very non-intuitive until you've implemented the protocols on several things and used datafy/`nav` with, say, Cognitect REBL.


Yeah, that gist is helpful -- note that both datafy and nav set up the next nav and datafy implementations respectively via metadata and the context of the "database" is carried through them as a closed-over binding.


Here's what next.jdbc adds (optionally): -- beyond the regular datafy/`nav` on result sets that it provides out of the box (that does FK navigation through the DB).


(excuse my terrible ascii art skillz @raymcdermott!)

😂 2

What a k + v navs to is your responsibility


☝️:skin-tone-2: Right, nav can use coll and/or k and/or v or ignore any or all of them. [updated to add coll!]


This is why it's a bit misleading to try to use datafy and nav with pure data and try to deduce their intent from that because (datafy d) is just d if it is pure data, and (nav d k v) is just v in that case.


nav can even ignore coll , like in the gist I linked to above. The reason, as @seancorfield points out, is that the db serving as the collection is closed over in the datafy method


In next.jdbc there's a datafy namespace that adds datafy/`nav` to a whole bunch of JDBC classes so you can navigate around the metadata of your database (or the metadata of your result set) and you can navigate across connections and statements and so on.