sql

athomasoriginal 2025-05-22T00:27:28.672789Z

I’m using sqlite and I have a multi-tenant architecture (1 db per user). There is a central db and a tenant db. My ds always has the central db, but I need to attach (attach database x as tenant) to the db connection. What’s the recommended approach to ensuring that the tenant db is always attached? Currently, I don’t use connection pooling, so I’m call (execute ds stmt) whenever I’m talking to the db.

seancorfield 2025-05-22T02:15:51.957709Z

What do you mean by "attach" in this case?

athomasoriginal 2025-05-22T02:24:17.428959Z

I have a ds which will connect to a central sqlite db {:dbname db-central.sqlite} and then when I know the tenant I can attach another database by running the sqlite statement ATTACH DATABASE 'db-tenant.sqlite' AS tenant . When I do this, I can run sql statements against either the central or the tenant db without having to create two separate connections.

seancorfield 2025-05-22T02:30:19.305619Z

Ah, I see... there's no support for that in next.jdbc. I guess I'd recommend writing a wrapper that does the ATTACH and then does whatever op you need?

seancorfield 2025-05-22T02:30:48.657259Z

(although, personally, I'd probably maintain a connection per tenant)

athomasoriginal 2025-05-22T02:31:33.544679Z

Through a connection pool?

seancorfield 2025-05-22T02:32:11.649769Z

No, since they're all different DBs. A connection pool only makes sense for a single, specific DB.

seancorfield 2025-05-22T02:33:11.106549Z

I mean, I might have a connection-pooled datasource for each tenant, but it really depends on traffic levels/patterns.

athomasoriginal 2025-05-22T02:36:45.942759Z

I’d probably maintain a connection per tenantwould these be stored in a global var? I’m trying to picture the architecture. Right now, I pass in a ds at the top level and then create a connection whenever it’s needed. Is the suggestion that I would create a connection and then store it and when said tenant comes back, I grab their connection? or perhaps something less involved?

seancorfield 2025-05-22T02:38:32.099819Z

It really would depend on traffic levels/patterns. But I'd probably have a hash map from tenant name to <something>, with an entry for :central as well. How exactly the selection is made for any specific operation would depend on how the code handles tenants today...

seancorfield 2025-05-22T02:39:21.856069Z

I like Component a lot for managing stateful resources (like DB connections/pools) so I'd probably have that in the mix somewhere.

athomasoriginal 2025-05-22T02:45:31.660689Z

General question: what closes the connections? My understanding is that they stay alive for as long as the application in running, but is there a reason/need to close connections after a while (sqlite may be different because it’s a file on the same server as the app runtime). My assumption is that when you noted maintain a connection per tenant you also meant to keep the connections alive (for perhaps as long as the user is active for example)

seancorfield 2025-05-22T02:47:20.336279Z

It depends on a lot of things.

seancorfield 2025-05-22T02:48:31.569929Z

At work, we use MySQL and connection pools via HikariCP. We pass around a datasource -- a connection pooled ds from HikariCP -- and we just pass it to next.jdbc which takes care of getting a connection (from the pool) and closing it afterward (returning it to the pool).

seancorfield 2025-05-22T02:49:12.755859Z

If we want to keep a connection around for a while, we use with-open on it, per next.jdbc docs.

seancorfield 2025-05-22T02:49:37.476249Z

(or with-transaction in the very rare cases we want an actual TX on a connection)

Mark Wardle 2025-05-22T09:58:12.940029Z

Hi all. Is there a built-in helper to avoid duplicate left-joins in honey sql. Further information in thread....

Mark Wardle 2025-05-22T09:58:35.070899Z

I'm using honey sql to dynamically build up some SQL based on user input. I really like the way the helpers automatically combine e.g.

(-> (h/select :id) (h/select :name))
=> {:select [:id :name]}
I have some logic that adds both select and inner joins, and in one case, the same left join might be added twice:
-> (h/select :id) (h/select :name) (h/left-join :wibble [:= :wibble-fk :wibble/id]) (h/left-join :wibble [:= :wibble-fk :wibble/id]))
=> {:select [:id :name], :left-join [:wibble [:= :wibble-fk :wibble/id] :wibble [:= :wibble-fk :wibble/id]]}
Is there a built-in way to not duplicate the left join, or do I need to check for the existence of the key manually and 'update' using plain old Clojure data structures?

p-himik 2025-05-22T10:23:53.353929Z

FWIW I don't remember there being any such helpers. I were in the same situation once and decided that using HoneySQL data as an accumulator for a process with many independent steps should be avoided if feasible. It's better to come up with something closer to your domain and approach and then, when such a piece of data is fully ready, turn it into a HoneySQL map.

Mark Wardle 2025-05-22T10:29:56.000569Z

That's a good idea. I can then have special logic to add a single left join if and only if it is needed, and ensure I'm not adding it twice. I was starting from smaller independent helpers and building up, but starting from the top down is a better idea. Thanks!

👍 1
igrishaev 2025-05-22T11:09:13.855829Z

I think you need the cond-> macro that updates the data structure conditionally as follows:

(cond-> {:select ...}
    
    (or is-this? is-that?)
    (add-left-join)
    
    is-that?
    (add-select-fields)

seancorfield 2025-05-22T12:30:14.728459Z

There's a #honeysql channel FYI

Mark Wardle 2025-05-22T13:25:45.294779Z

Thanks Sean didn’t spot that thank you.