Hi Pablo
I think the reason you've got so many responses is because most people
share the same problem. I do think this is a fruitful area of discussion.
There are multiple ways to go about it.
Based on your original post, I'll share my two cents:
In my experience, mixing an atom and
On 5 August 2015 at 19:33, James Reeves ja...@booleanknot.com wrote:
So when you're testing, presumably you use a dynamic binding to override
the global connection to the test database?
The wrap transaction always overrides the dynamic binding, whether it's in
tests or not.
--
J. Pablo
On Monday, August 3, 2015 at 5:19:34 AM UTC-5, Colin Yates wrote:
I have heard this approach before, but I have never seen how it works in
real life.
Interesting. I don't think I've ever worked anywhere that didn't take this
kind of approach. Even if we weren't doing it formally.
For
On Monday, August 3, 2015 at 10:21:09 PM UTC-5, Dmitri wrote:
My understanding is that the problem is actually caused by the stateless
nature of the functions.
You're talking about database interactions. By definition, those are not
stateless. The trick is to isolate this statefulness as
I think we are talking at cross purposes - I thought you were talking
about delaying the mutation until the 'main' logic has finished, whereby
the main logic would push the mutation into a queue which were then
executed later. Something like queueing up a bunch of commands to be
executed later.
What I'm talking about is whether it's a better pattern to leave a
repetitive and error prone task to the user or encapsulate it in a single
place. The whole discussion boils down to following options.
The first option is that we keep functions pure and the connection is
passed as a parameter
On 5 August 2015 at 14:03, Dmitri dmitri.sotni...@gmail.com wrote:
What I'm talking about is whether it's a better pattern to leave a
repetitive and error prone task to the user or encapsulate it in a single
place. The whole discussion boils down to following options.
The first option is
I agree that wrapping the functions is a sensible approach. Using
wrap-transaction is precisely how I ended up doing it with the conman yesql
wrapper https://github.com/luminus-framework/conman
The approach I took there is to have the generated functions use the
connection atom, and have
This.
I would repeat with as strong an emphasis as possible the sanity
of using a dedicated test database - how else can you test schema
creation for example...
Dmitri writes:
I agree that wrapping the functions is a sensible approach. Using
wrap-transaction is precisely how I ended up doing
On 5 August 2015 at 18:04, Dmitri dmitri.sotni...@gmail.com wrote:
I agree that wrapping the functions is a sensible approach. Using
wrap-transaction is precisely how I ended up doing it with the conman yesql
wrapper https://github.com/luminus-framework/conman
The approach I took there is to
There are a number of options here depending on your workflow. You could
override the dynamic var, create a separate connection for tests and pass
it around explicitly in test code, or get the connection from the
environment. The last option is what I tend to do, the profiles.clj will
contain
On 3 August 2015 at 23:47, Rob Lally rob.la...@gmail.com wrote:
I’m also a little confused by your suggestion that it would be impossible
to enclose each test in a transaction. The article you point to shows one
way.
It shows how to pass data to the test by using a dynamic var, which is what
Everything you say is true. And programming is always about picking the right
trade-offs at the right time, making this sort of conversation one that can
never come to a *right* answer.
But… in this case Pablo was experiencing concrete problems: those problems
stemmed from how easy it was to
Hey Pablo,
Sorry, you are completely correct: I accidentally typed transaction when I
meant connection. My bad.
I don’t understand what you mean by connections having to be global when
dealing with an RDBMS. It is true that you normally want to pool them to
conserve resources, and that
While I generally agree that purity is something to strive for, I think
it's also important to consider the problem that's being solved in each
particular scenario. While creating stateless database query functions and
passing the connection round explicitly achieves purity, it's not entirely
My understanding is that the problem is actually caused by the stateless
nature of the functions. Since the function accepts the connection as a
parameter it's up to the user of the function to ensure that it's passed
the correct connection. Every functional solution presented in this thread
I have heard this approach before, but I have never seen how it works in
real life. For example, what about 'selects' - where do they happen?
What about if my updates are not independent (e.g. if the first
update works then do the second update otherwise do this completely
different thing?).
For
Other than the fact that this approach doesn't reach the level of
functional purity that some people want, after playing with it for a while,
we found it very productive and leads to clean/readable code and tests, so
we decided to turn it into a library:
https://clojars.org/conman
Rob,
The transactions are not global, the transactions are local. Connections
are global and there's no way around it with the constraints of a
traditional RDBMs. Your idea of making the global connection unavailable
for code that's in the context of a transaction would prevent the errors
I'm
James,
I'm not new to functional programming and I understand the principles and
why they are good. I worked in Haskell, Erlang and other Lisps before. Even
if only a tiny portion of my codebase deals with the database, I still need
a pattern for that part of the codebase.
It is very easy to say
On Thursday, July 30, 2015 at 7:44:31 PM UTC-5, J. Pablo Fernández wrote:
Hello Clojurians,
I found passing around the database connection to each function that uses
it very error prone when you are using transactions as passing the wrong
one could mean a query runs outside the
Hello,
I think you are coming to a point where you might want to consider Stuart
Sierra's component library.
https://github.com/stuartsierra/component
A dynamic var is kind of thread-bounded global state, which is to be
avoided.
There is a nice talk to go with it
Hey Pablo,
I could be wrong, but it seems that the key problem here is the existence of
the global transaction. If the global transaction didn’t exist then any time
you failed to pass in a transaction the code would fail: immediately and loudly.
I appreciate what you’re trying to do but it
On 1 August 2015 at 01:38, jongwon.choi oz.jongwon.c...@gmail.com wrote:
That's how I normally use dynamic vars. Some people afraid of using it,
but it is like a knife - If you know how to use it, it is useful to remove
unnecessary complexity.
Well, it's more that dynamic vars trade
That is indeed a useful library for wiring things up at init-time, but it
doesn't help at all for run-time wiring, like transactions.
On 1 Aug 2015 07:33, Jo Geraerts j...@umask.net wrote:
Hello,
I think you are coming to a point where you might want to consider Stuart
Sierra's component
Cut paste from my own code base:
(def ^:dynamic *db-conn*)
(defmacro with-db-connection [ body]
`(jdbc/with-db-connection [con# (get-db-conn)]
(binding [*db-conn* con#]
~@body)))
A few points. Do not give any value to the dynamic var, make it unbound and
make it fail early for
On Friday, 31 July 2015 11:48:54 UTC+1, Colin Yates wrote:
I am away from the code at the moment, but is there any reason why the
dynamic connection can’t be private? This goes some way to providing safety.
No, I don't think there's a problem with it being private, I just didn't
think of
On 31 July 2015 at 09:54, J. Pablo Fernández pup...@pupeno.com wrote:
For me, the thing is, I have a traditional relational database here, this
is already far from pure. For example, calling (db/create-user
pup...@pupeno.com) twice will not only not return the same thing the
second time,
Hello James,
Thanks for your answer. I do understand your point. Pure functions are
easier to reason about and my use of dynamic here breaks that purity. I'm
not doing it lightly. It already happened to me, that one of those
functions that was running inside the transaction, was not passed the
This is always tricky - the other dimension is that the transaction can span
multiple components which should all be ignorant of each other, so a collecting
fn (like James’ ‘do-things*’) isn’t feasible.
For me, I have the service create a tx and then either I pass in the tx instead
of the db
Hello Clojurians,
I found passing around the database connection to each function that uses
it very error prone when you are using transactions as passing the wrong
one could mean a query runs outside the transaction when in the source code
it is inside the with-db-transaction function. So I
On 31 July 2015 at 01:44, J. Pablo Fernández pup...@pupeno.com wrote:
I found passing around the database connection to each function that uses
it very error prone when you are using transactions as passing the wrong
one could mean a query runs outside the transaction when in the source code
32 matches
Mail list logo