Fork me on GitHub
#clojure
<
2024-04-07
>
mogverse05:04:29

Has anyone played around with http://temporal.io and Clojure and refactored existing code as workflows and activities? (If yes, how was your experience?)

😲 1
mogverse05:04:07

I discovered #C056TDVQ5L1. Reading through the content there.

Bailey Kocin14:04:55

I have! It was not bad at all! Took little time refactoring the code. The hardest part was utilizing the Temporal model and optimizing our configuration to get the most out of the Temporal runtime. Temporal runs stuff pretty easily The ‘temporal-clojure-sdk’ was a big help as well!

👍 1
mogverse03:04:41

@U05NZDGDYG3 Did you get this feeling while doing so, that a regular codebase that doesn't use temporal can also benefit from having different naming convention for what's a workflow, side effect and what's an activity. And you could potentially roll out your own retry mechanism if you can distinguish between these?

Bailey Kocin16:04:39

I think if you are using Clojure these things just tend to happen naturally because we already have naming conventions for functions with side effects vs pure functions. That is why I think Clojure lends itself to just being easy to migrate to Temporal since everything is already organized as a "function"

vemv11:04:22

Anyone has hacked Component such that each component receives other components (i.e. its dependencies), already started? It's a pattern that I got used to from Integrant usage

vemv12:04:45

e.g. given Component Foo that depends on Bar , I want that after Foo is started, it gets the started instance of Bar assoced into it All automated and following the system's dependency order

Ben Sless12:04:47

Stupid question - why? It's sort of what happens on components' start, where every dependency is updated with start before the parent

vemv12:04:26

Following my previous example, if an instance of Foo can access the instance of Bar by just accessing (:bar this) - it's simply more ergonomic to use throughout the app, I believe i.e. in the codebase, functions always depend on exactly one component which seems better than: • N positional arguments (one per component) • the whole system • an ad-hoc subsystem (slice of the system) without a formal definition

cjohansen12:04:27

Why not just use integrant?

vemv12:04:15

Because reasons®

Ben Sless12:04:25

But it can, that's the guarantee of component

Ben Sless12:04:51

In component, when a specific component starts, all its dependencies are already started and assoced into it

Ben Sless12:04:12

Half the time I don't use records for components, I just go with maps and metadata

Ben Sless12:04:32

Even pulled this pattern out to a little helper lib https://github.com/bsless/companion

vemv12:04:32

Nice, I fully agree on your project's problem statement (which is a more proper answer to @U9MKYDN4Q's question) > In component, when a specific component starts, all its dependencies are already started and assoced into it I probably just forgot about this, long time no Component ...I'm in the middle of a big project cleanup, so a dozen things were broken, so at times it's hard to distinguish "truly broken" from "faulty refactor" 😇

vemv12:04:36

btw whenever I can use Component, I use metadata-based protocol extension, which is fun enough, among other advantages. Probably if it was there 10 years ago we wouldn't have had the same explosion of alternatives

Ben Sless12:04:19

Another thing I adopted using component is making a component for everything that can be an argument

vemv12:04:35

That's nice 👍 Although I used that fine-grainedness earlier this year with Integrant earlier this year and it made me nervous tbh. I like understanding a System as a moderate number of modules with clear responsibities. Anything smaller than that might as well be considered config? With aero or whatnot

Ben Sless12:04:37

Funny you should mention aero, because aero and component work very well together

Ben Sless12:04:09

and having the same "shape" for your system and config allows you to (merge-with merge config system) just like that

Ben Sless12:04:17

and have no code to pick apart configuration

Ben Sless12:04:21

It's always a source of subtle bugs

Ben Sless12:04:24

A couple of things I dislike is any digging into config at runtime or a component digging into its children

vemv13:04:55

> and having the same "shape" for your system and config allows you to (merge-with merge config system) just like that Yeah personally I disagree with that goal, I tend to like code and data separated (System = code, config = data)

Ben Sless13:04:20

But code is data 🙂

Ben Sless13:04:30

I'm playing around with the idea of moving as much of the system's configuration to data

Ben Sless13:04:47

You can also move all the using spec to data

vemv13:04:21

But code is dataYeah, there's always that objection, but even in Clojure itself there are quite a few exceptions - it has misc affordances which make it not map 1:1 to an AST? Or in any case code-as-data tends to be unsafe and complected. Of course there's a path to convert one to another, but they (IMO) should remain sharply distinct.

vemv13:04:27

...back to my OP, I had this bug:

- component/start
+ component/start-system
which is why I was seeing an incomplete system, I believe.

Ben Sless13:04:27

Are you using system-map?

Ben Sless13:04:30

(defrecord SystemMap []
  Lifecycle
  (start [system]
    (start-system system))
  (stop [system]
    (stop-system system)))

vemv13:04:22

Probably I was using system-map with some modifications on top Anyway, I got past these issues and am closer to enjoying Component again :)

🎉 2
igrishaev18:04:16

> if an instance of Foo can access the instance of Bar by just accessing (:bar this) - it's simply more ergonomic to use throughout the app, I believe

igrishaev18:04:22

I believe this is a wrong approach which will let you down. If component C needs A and B, and B depends on A, it's wrong to fetch B from A. Just add B and A as dependencies to C. Also, a component might act as a function if you implement IFf interface, namely:

(defrecord Myhandler [A B]
  clojure.lang.IFn
  (invoke [this request]
    (do-something-with A)
    (do-something-with B)
    {:status 200 :body "OK"}))

igrishaev18:04:04

Then you add an instance of Myhandler to the system, and when started, it gets populates with started A and B

seancorfield23:04:31

@UK0810AQ2 companion looks nice -- no releases to Clojars, so it's git-dep only? We often implement IFn in our components but mostly so that (my-component) => state -- implementing a "sensible" function of state, such execute! or execute-one! on a datasource, is smart. We have a configuration component that implements 1- an 2-arity IFn so (cfg :k) and (cfg :k default) work as keyed fetches from the state (the config map).

Ben Sless02:04:20

There are a couple of things I need to wrap up in companion before cutting a release and other things had higher priorities, so it kept getting pushed back. It does what I need it to, but without fleshing it out and a decent guide with examples I wouldn't recommend using it yet

1
respatialized15:04:35

Protocols question: if I am using a library that relies on protocols, can I add a new method to already-defined protocols? Or do I need to define a new protocol and "inherit" from the protocol I am trying to extend by repeating the implementations? Edit: here's a simplified example of how I'm thinking about it:

(defprotocol MyProto
  (foo [arg] ...)
  (bar [arg] ...))

(add-method MyProto
  (baz [arg] ...))
am I thinking about protocols incorrectly here?

Alex Miller (Clojure team)16:04:22

You can just implement the existing protocol and a new protocol, you don’t need inheritance

3
emccue13:04:57

To expand on that, if you need to implement a new protocol on anything that implements an existing protocol you can do a little hackery like so

emccue13:04:25

(defprotocol MyProtoA
  (foo [arg] ...)
  (bar [arg] ...))

(defprotocol MyProtoB
  (baz* [arg] ...))

(defn baz
  [o]
  (if (satisfies? MyProtoB o)
    (baz* o)
    (... (foo o) ... (bar o) ...)))

emccue13:04:33

though obviously that is very context dependent and you probably don't want or need that

Alex Miller (Clojure team)14:04:21

there are other ways even to get that effect, but you don't even need that - if you're making new stuff, make a new protocol

Magnus20:04:16

TCP TLS question: I fiddled a bit around today with TCP sockets in Aleph, and I want to test data streams between multiple different computers, just because fun. The thinking is that Aleph deals with the streams and TCP is natural for custom arbitrary data IO. But I've spent way too much time trying to get TLS up and running. Best examples I found was in https://github.com/clj-commons/aleph/blob/master/test/aleph/tcp_ssl_test.clj, but its not 'real' certificates, as in certificates generated by openssl and signed by your CA, the demo is using some other format. I just want to use some self-signed certs to see how the prod version would be. Part of my time was spent reading up on openssl and generating the certs. 15 years of changing guides, so a bit of pain there. Has anybody seen a good guide for TCP + TLS + Aleph ?

Ivar Refsdal13:04:02

Have you seen https://github.com/aphyr/less-awful-ssl/ ? It's a bit dated, but hey... that library gives you an JDK SSLContext, which you can pass to netty (JdkSslContext last time I checked). I wrote a tiny lib for generating keys: https://github.com/ivarref/locksmith for using with nREPL TLS support. Some info on the nREPL TLS page: https://nrepl.org/nrepl/1.1/usage/tls.html Not sure how much that helps, but who knows? 🙃 The basic functionality of less-awful-ssl is replicated in nREPL TLS, but updated to use only TLS v1.3

Magnus18:04:13

Thanks! Tried for a couple of evenings, but too damn fiddly. I'll keep an eye out for anybody doing it in the future. I'm left more confused than anything 🤷

🧡 1
Ivar Refsdal20:04:59

there is also https://github.com/FiloSottile/mkcert for making self signed certs and trusting them for local development

Linus Ericsson21:04:23

step-cli also makes it much easier to generate your own certificate structure, an example: https://github.com/claj/reproduce-pedestal-issue-38/blob/master/generate-pki.sh