Fork me on GitHub
#clojure
<
2022-11-23
>
kah0ona07:11:34

Hi Folks, I have a function call with side effects, somewhere in my algorithm, and i don’t care about the result value It’s pretty slow however, so I’d like to just fire-and-forget it, is a wrap in future the best way to do this?

Hermann08:11:05

It would work on futures, but futures cache the result and thus will aggregate memory if not disposed of carefully. A better way might be to simpliy (.start (Thread. (fn [] (println "hello"))). As a word of warning, I used a similar approach in the past and went back to awaiting the success of the side effect, as my main code overtaking the side effects started causing race conditions.

lispyclouds08:11:07

That's what i'd do as well. if you care about it completing you could add a @f to await completion somewhere where (def f (future ...))

kah0ona08:11:44

Thanks, in both approaches, what would happen if

kah0ona08:11:23

I run this in a doseq, potentially firing off like a dozen of these calls? would that cause things to choke, ie. halting the whole webapp

kah0ona08:11:25

(I’m doing some performance tweaks on a particular slow part in my webapp, that gets triggered when a user clicks a button)

lispyclouds08:11:33

if you deref inside the doseq, then yes it would block that thread. webapps should work around it with more threads but its a resource hog

Hermann08:11:20

As always, that depends on the machine, the length of the seqs,... but basically I agree with listpyclouds

kah0ona08:11:23

what do you mean with ‘work around it with more thread’. Or would that mean using channels/go-loops/parking etc

Hermann08:11:52

When you're speaking about a webapp, you mean the backend part, right?

lispyclouds08:11:04

so a webserver on the JVM works with a threadpool, assigning a request to a thread. when the doseq happens that thread blocks

kah0ona08:11:04

server side, Ring based

lispyclouds08:11:37

more requests should have other free threads to execute

Hermann08:11:15

Every doseqq will block a thread, so that is a factor to consider

👍 1
lispyclouds08:11:22

if too many such requests happen, you could block the pool completely and/or run out of RAM

lispyclouds08:11:20

Project Loom with Virtual Threads from JDK 19 aims to solve these, specially when theres a lot of waiting on external things like IO.

👍 1
kah0ona08:11:49

(defn my-req-handler
 [req]
 (let [db     (get-connection) 
       orders (generate-orders)]
  (doseq [o orders]
   (orderdb/create-order! o db))))

kah0ona08:11:01

So in essence, the ‘create-order’ call is slow

kah0ona08:11:12

And it generates about 10-20 orders in that doseq

kah0ona08:11:02

of course, there’s batch insertion on the database level, but for the future, i’d like to know a general strategy for dealing with this

kah0ona08:11:13

Re: project loom: how would that translate to clojure/Ring? or will it ‘just work’ (tm)

Benjamin C08:11:53

I haven't tried either of these, however: If you're using vm >= 19, https://github.com/teknql/tapestry may be a nice solution. https://funcool.github.io/promesa/latest/index.html also recently added support for loom virtual theads.

lispyclouds08:11:01

just work(tm) is the aim of it, Jetty 10 has experimental support for it

kah0ona08:11:32

ah ok, i’m using http-kit btw

kah0ona08:11:12

we run an older VM atm, but we ’re thinking of upgrading most of our infrastructure, so this is good to consider

lispyclouds08:11:37

re: blocking txns, being very pedantic, id do it in a separate service and connect it via a queue. return an id of the txn, allowing the caller to check status asynchronously

lispyclouds08:11:09

that way the caller never blocks and you can handle failures gracefully

Hermann08:11:15

I second the queue thing but was missing the part with a checkable ID

kah0ona08:11:27

yeah, trade off being more involved code

kah0ona08:11:40

and you mean, separate service, as in, separate machine/app right?

kah0ona08:11:50

separate process even

Hermann08:11:12

But gain is f&f and non-race-conditional behaviour, no overgrowing thread pools

kah0ona08:11:14

because, if it’d be in the same app

lispyclouds08:11:46

yeah its a bit more code but has much more reliability guarantees

kah0ona08:11:01

then the problem remains? or will the queueing system manage its own threads making sure it’s not clogging the system

kah0ona08:11:17

I agree, it ’s probably the best direction to take

Hermann08:11:19

The service can be another app or just another thread

lispyclouds08:11:34

id off load it to something like rabbitmq or sqs

kah0ona08:11:45

ah yeah i figured that yes

kah0ona08:11:38

could it be done inside the application, with ie. some queueing lib that manages its own resources?

lispyclouds08:11:39

the clojure way de complect to simpler parts 😄

kah0ona08:11:10

yeah i know… but the code of the doseq is so simple 😄

kah0ona08:11:16

or is it easy

kah0ona08:11:20

it’s probably easy

lispyclouds08:11:05

before doing it all, id say deploy the app, gather some metrics and measurements, both human and machine and then have specific actions?

lispyclouds08:11:43

something like https://github.com/layerware/pgqueue comes to mind as starters

msolli10:11:10

At work, we do this all the time. We use a queue to manage these “jobs”. In your example, the whole doseq form would be a job. https://github.com/msolli/proletarian is the job worker system we use (only for Postgres, though).

kah0ona07:11:35

or do I ‘have’ to work with chan’s/queues etc.

pavlosmelissinos08:11:40

Hello! Can someone tell me how these are not inconsistent?

(merge {:foo :bar} nil)
;;=> {:foo :bar}

(merge {:foo2 {:foo :bar}} {:foo2 nil})
;;=> {:foo2 nil}
I think I would expect the following output from the second example (but I'm sure I'm missing something):
{:foo2 {:foo :bar}}
edit: the docstring describes what is actually happening so it's definitely not misleading, I was just curious about the design decision

tatut08:11:28

{:foo2 {:foo :bar}, :foo nil} is what I get from the second, which looks right

tatut08:11:58

are you referring to something other than clojure.core/merge ?

p-himik08:11:07

Yeah, same:

Clojure 1.11.1
user=> (merge {:foo2 {:foo :bar}} {:foo nil})
{:foo2 {:foo :bar}, :foo nil}

pavlosmelissinos08:11:51

Dang, I had a typo, sorry:

(merge {:foo2 {:foo :bar}} {:foo2 nil})
;;=> {:foo2 nil}

grav08:11:14

It's not a deep merge

magnars08:11:15

There are a couple things going on here. First is that merge is not a deep merge. It's just a single map being merged into another single map. You might be looking for deep-merge , which can be found for instance in medley. Second is that nil is not an empty map, so even with a deep merge, that nil will override whatever is in :foo2 ... this can be worked around by using empty maps instead.

pavlosmelissinos08:11:15

Alright, in that case why does this work?

(merge {:foo :bar} nil)
;;=> {:foo :bar}
By that reasoning why isn't the result nil here as well? Thanks for the suggestions but I know how to work around this, it's not really a practical problem 🙂

magnars08:11:12

Basically because merge takes a map and merges other maps into it. Clobbering the original map with a stray nil would be decidedly impractical.

👍 1
pavlosmelissinos08:11:52

Alright, so some minor inconsistency for much greater practical value. Makes sense and I can definitely live with that! To give you some context, we actually have a deep-merge at work that I want to modify slightly and I was trying to understand why merge was designed like this first.

👍 1
😊 1
tatut09:11:25

I don’t think it is inconsistent, a map is also a sequence of map entries, which nil is an empty sequence of

💡 4
viesti12:11:24

Hmm, I'm trying to use DynamoDB local via deps.edn with

com.amazonaws/DynamoDBLocal {:mvn/version "1.20.0"}
which has a dependency to platform-dependent native libraries for sqlite (which that local implementation uses), for example, the OSX dependency is:https://central.sonatype.dev/artifact/com.almworks.sqlite4java/libsqlite4java-osx/1.0.392 trouble is that the native libs don't get downloaded, I guess maven somehow can look into this <packaging> thing
<groupId>com.almworks.sqlite4java</groupId>
  <artifactId>libsqlite4java-osx</artifactId>
  <packaging>dylib</packaging>
but don't know how to do that with deps.edn (slapping com.almworks.sqlite4java/libsqlite4java-osx$dylib {:mvn/version "1.0.392"} didn't work)

Hendrik12:11:11

“…java-osx@” I assume, that you are using a mac? If so is it an ARM-based M1? I had trouble in the past with some libraries. For some libraries I needed a x86 Java

viesti12:11:20

learning maven, seems there is this packaging thing: https://maven.apache.org/pom.html#packaging

viesti12:11:32

this is intel mac, though I have arm one too

Hendrik12:11:57

Is the sqlite binary for arm or x86 architecture?

Hendrik12:11:24

ok its 8 years old. So probably x86

viesti12:11:50

libsqlite4java-osx is for x86, there's libsqlite4java-osx-arm64/aarch64 for arm

viesti12:11:53

putting

<dependency>
       <groupId>com.amazonaws</groupId>
       <artifactId>DynamoDBLocal</artifactId>
       <version>1.20.0</version>
    </dependency>
...
  <repositories>
    <repository>
      <id>dynamodb-local-oregon</id>
      <name>DynamoDB Local Release Repository</name>
      <url></url>
    </repository>
  </repositories>
into a maven project, makes it download native libs too:
0% tree ~/.m2/repository/com/almworks/sqlite4java/libsqlite4java-osx
/Users/kimmoko/.m2/repository/com/almworks/sqlite4java/libsqlite4java-osx
└── 1.0.392
    ├── _remote.repositories
    ├── libsqlite4java-osx-1.0.392.dylib
    ├── libsqlite4java-osx-1.0.392.dylib.lastUpdated
    ├── libsqlite4java-osx-1.0.392.dylib.sha1
    ├── libsqlite4java-osx-1.0.392.pom
    ├── libsqlite4java-osx-1.0.392.pom.lastUpdated
    └── libsqlite4java-osx-1.0.392.pom.sha1

viesti13:11:39

but a deps.edn thing doesn't download native libs

0% cat deps.edn
{:deps {com.amazonaws/DynamoDBLocal {:mvn/version "1.20.0"}}
 :mvn/repos {"dynamodb-local-oregon" {:url ""}}}
0% clj
Downloading: com/almworks/sqlite4java/sqlite4java-win32-x64/1.0.392/sqlite4java-win32-x64-1.0.392.pom from central
Downloading: com/almworks/sqlite4java/libsqlite4java-osx/1.0.392/libsqlite4java-osx-1.0.392.pom from central
Downloading: com/almworks/sqlite4java/sqlite4java-win32-x86/1.0.392/sqlite4java-win32-x86-1.0.392.pom from central
Downloading: com/almworks/sqlite4java/libsqlite4java-linux-amd64/1.0.392/libsqlite4java-linux-amd64-1.0.392.pom from central
Downloading: com/almworks/sqlite4java/libsqlite4java-linux-i386/1.0.392/libsqlite4java-linux-i386-1.0.392.pom from central
Downloading: com/almworks/sqlite4java/sqlite4java/1.0.392/sqlite4java-1.0.392.pom from central
Downloading: com/almworks/sqlite4java/sqlite4java/1.0.392/sqlite4java-1.0.392.jar from central
Clojure 1.11.1
user=>
0% tree ~/.m2/repository/com/almworks/sqlite4java/libsqlite4java-osx
/Users/kimmoko/.m2/repository/com/almworks/sqlite4java/libsqlite4java-osx
└── 1.0.392
    ├── _remote.repositories
    ├── libsqlite4java-osx-1.0.392.pom
    └── libsqlite4java-osx-1.0.392.pom.sha1

1 directory, 3 files

viesti13:11:30

so maybe something interesting going on with that packacing thing

viesti13:11:57

maybe I should ask this in #C6QH853H8

Alex Miller (Clojure team)14:11:21

Tdeps only supports jar packaging right now

✔️ 1
practicalli-johnny13:11:44

Any suggestions as to content I could add to the http://clojure.org website? I guess this is partly for @alexmiller but interested in other opinions. There seems to be https://github.com/clojure/clojure-site/issues, so I'll take a look at those as well. I am on gardening leave from my current role, so have a bit of time until the end of the year (assuming no one tries to hire me 🙂 )

Rupert (All Street)16:11:30

Would recommend checking out some other popular languages websites and seeing what ideas we can borrow from them e.g. • https://www.python.org/https://go.dev/https://www.scala-lang.org/https://www.rust-lang.org/ A few ideas that I think would be good: • Show some example code (I clicked around and didn't find any very easily!) • Demonstrate that it is a successful and growing language • Show that it is easy to learn • Demonstrate that Clojure is the future of programming and programmer productivity • Demonstrate that is is widely used and a good choice for teams that want to get ahead of their competition. • Compare it to other languages and show why it is better. • Show off the cool libraries and things built with Clojure

Alex Miller (Clojure team)16:11:17

I don't think we need to open yet another thread on this. we've already done this analysis, we have a big list of ideas and todos for the front matter, some that have been acted on, some that will be worked on as we have time. I don't think it's easy for people outside the core team to work on this as it really requires direct collaboration (and we're mostly on holiday this week and working on other things right now)

👍 1
practicalli-johnny18:11:46

Okay Alex thanks for the update, sorry I missed that thread. I focus on the Practicalli TODO list instead. Thanks.

Benjamin17:11:33

Isn't a set the more logical datastructure for the coll keyseq in select-keys ?

Alex Miller (Clojure team)17:11:28

the argument passed to select-keys is called keyseq - it can be anything seqable

Alex Miller (Clojure team)17:11:14

user=> (select-keys {\a 1 \b 2} "ab")
{\a 1, \b 2}

clojure-spin 1
Benjamin17:11:31

that is so sick

Benjamin17:11:45

I also keep forgetting vecs are associative 😅

(let [word "hello"]

(select-keys (vec word) (filter even? (range (count word)))))
;; {0 \h, 2 \l, 4 \o}

peterh17:11:28

Is there a practical limit on how long keywords can be in Clojure? It seems like they can easily handle millions of chars (the internal representation is a string, after all), but are there any known problems with keywords in the million character range other than performance?

Alex Miller (Clojure team)17:11:38

Keywords are interned so you’re keeping memory for anything you make

Alex Miller (Clojure team)17:11:59

They are reclaimed if unreferenced and under memory pressure but…. if you’re in that situation, you’re probably in bad shape already

lread17:11:26

And keywords that are millions of chars long are really hard to remember and type.

🙃 2
peterh18:11:24

@alexmiller: is this what you mean with interned: “For keywords, hash values are calculated and cached when the keyword is first constructed. When looking up a keyword as a hash key, it simply returns the precomputed hashed value.” ?(quoted from: https://stackoverflow.com/a/31503297/1204047 ) The keywords are used as codes/values of expressions for what I am trying to do, so it may actually help to have them cached and be able to do fast equality checks, although they would be mostly in the range of 10k chars and very rarely >1 Mio.

Joshua Suskalo18:11:43

It seems like a very strange usecase for keywords to be long. The intended usecase for keywords is to act as names for things to be referenced in your code. What are the aspects of keywords that you need for your project that wouldn't be handled just as effectively by strings and an LRU cache from core.cache?

Alex Miller (Clojure team)18:11:09

By interning, I mean caching. But this seems like an abuse of the keyword cache and likely to eventually leave you someplace bad. If you want a cache or an object pool, I think you’d be way better off making one explicitly so you could control it

Alex Miller (Clojure team)18:11:40

Keyword equality checks are fast because they are identity checks, but that’s a property you can also get with other jvm values. String interning is likely also relevant here and string deduplication in jvm gc’s may help you but there a lot of factors likely to influence that

peterh18:11:54

Yeah I had a feeling that this may not be a good idea after all, so I guess strings may be the safer choice. The codes basically look like this: :NUIMNNIINUNUNNNNNUIMNUIMNUNUNUNUNUIMNNIINUIMNNIINUIMNUIMNUIMNUIM and most of them have a length of just 1 to 256 chars, but they can grow exponentially in 4^n. I have different functions that break them apart, transform or permute them for nested maps that act as value tables (which made me think about using keywords), embed them in Hiccup-like expressions and so on.

Joshua Suskalo19:11:09

You may actually want to look at something like https://github.com/IGJoshua/ropes or clojure's vector-of :char

Joshua Suskalo19:11:18

Depending on the exact operations that are performed

Joshua Suskalo19:11:42

ropes allow you to have a low memory footprint and high performance when your primary operations are splitting and re-combining sections of these long strings

Joshua Suskalo19:11:11

clojure's vector-of produces vectors that follow clojure's semantics for vectors but with a primitive representation to reduce memory usage and improve performance.

peterh19:11:15

Thanks for the suggestion, I will take a look at that! I am fairly new to data structures since I am self-thought and only recently went deeper into computer science, so I am also in permanent risk of premature optimization due to lack of experience. :)

Joshua Suskalo19:11:07

The ropes library doesn't have a formal public release or exported docs yet because I was planning on doing more with it before then, but it's in a robust enough state to at least be used for reference. I recommend them over vectors when you're doing large numbers of concatenations, splits, and other similar operations. You can depend on it with a git dependency.

Joshua Suskalo19:11:24

Unfortunately I haven't found any other libraries that provide the same functionality for Clojure as the only other ropes library is byte-ropes only.

peterh19:11:01

Seems like a good fit. I have seen in the GH issues that you are interested in porting it to cljc and since this is what I am using for my project (I plan to have compatibility with ClojureScript) I can only experiment with it for now and maybe use it later if you are still interested and have the time to implement cljc support.

Joshua Suskalo19:11:15

Yeah, the main issue that I have with CLJS support so far is that I don't know off the top of my head how to ensure that CLJS regexes will work with them. Knowing that someone is interested in ropes however can help push it up my priority list and get it CLJS support.

Joshua Suskalo20:11:34

Ah, at least from my research so far it is not possible to implement Regex compatibility in JavaScript with ropes, which means that the cljs variant will be somewhat hamstrung in terms of functionality as it will require you coerce it to a string before running regex functions over it.

peterh21:11:20

Oh, that is unfortunate. However, I think I’ll only consider such specialized data structures when my library reaches a point where it actually matters and then I may focus on Java for such performance-critical tasks anyway.

peterh21:11:13

When I think about it, maybe instead of strings it would be better in my case to use vectors of keywords, since the codes I represent only consist of 4 distinct values, so something like [ :N :U :I :M … ] could maybe take advantage of caching while only having to store these 4 values. And for the transformations I need vectors anyway, so no need to convert from strings all the time…

Joshua Suskalo21:11:50

That may be better, yes. This is actually also a supported usecase for my ropes library, but as you said it needs cljs support for your project.

Linus Ericsson11:11:27

If you have a limited number of symbols (it looks like you have only four symbols = 2bits per symbol), and are under memory pressure, maybe you should consider using one byte per symbol (which is wasteful, but anyway) or encode the symbols in bytes, or, my favourite - longs. But with this encoding you would have to mark how long the ”string” of symbols is in the long(or byte) if they are not the full length of the container. Then you can make concatenations with bit shifts/rotates and and-operations. Fun but quite elaborate.

Linus Ericsson11:11:51

Half way there would be to encode the symbol string in a bigint, but ensure that there is always a largest bit marking the end of the string that should not be part of the string itself

reefersleep11:11:51

The initial message in this thread had me thinking “Ah yes, we’ve made all sorts of other databases, why not KeywordDb?

😂 1
reefersleep12:11:25

Why use files when a single keyword can do the job? 🙂

icemanmelting18:11:31

Hey guys, does any of you know of a docker clojure image that contains the ability to use virtual threads?

icemanmelting18:11:01

I used clojure:openjdk-19-lein-2.9.8 and I get this exception Caused by: java.lang.ClassNotFoundException: java.lang.VirtualThread

1
Joshua Suskalo18:11:18

have you set the --enable-preview JVM flag on your program?

icemanmelting18:11:45

Yes I have, I used a new one just now clojure:temurin-19-tools-deps-jammy and it worked!

icemanmelting18:11:02

Thanks for your time

Joshua Suskalo18:11:21

Glad it worked!

ghadi18:11:37

fyi VirtualThread is not a public class, and code shouldn't directly try to load it

icemanmelting18:11:57

I am not, I am using a virtual thread factory

icemanmelting18:11:38

The error might be related with an import done automatically when i was trying to understand how everything worked