Skip to content

Commit d6df0ac

Browse files
authored
fix #53 (#55)
* fix #53 * fix get_database_version
1 parent 41e1ad8 commit d6df0ac

File tree

4 files changed

+38
-14
lines changed

4 files changed

+38
-14
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
### 1.1.3
22
- Fix #50 partition by single expression raise TypeError.
33
- Fix #51 .
4+
- Fix #53 .
45

56
### 1.1.2
67
- Use [flake8](https://flake8.pycqa.org/) to lint code.

clickhouse_backend/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from clickhouse_backend.utils.version import get_version
22

3-
VERSION = (1, 1, 3, "alpha", 0)
3+
VERSION = (1, 1, 3, "final", 0)
44

55
__version__ = get_version(VERSION)

clickhouse_backend/backend/base.py

+23-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import threading
2+
13
from django.conf import settings
24
from django.core.exceptions import ImproperlyConfigured
35
from django.db.backends.base.base import BaseDatabaseWrapper
@@ -16,6 +18,9 @@
1618

1719

1820
class DatabaseWrapper(BaseDatabaseWrapper):
21+
_connection_sharing_lock = threading.Lock()
22+
_clickhouse_connections = {}
23+
1924
vendor = "clickhouse"
2025
display_name = "ClickHouse"
2126
# This dictionary maps Field objects to their associated ClickHouse column
@@ -200,8 +205,17 @@ def get_connection_params(self):
200205

201206
@async_unsafe
202207
def get_new_connection(self, conn_params):
203-
connection = Database.connect(**conn_params)
204-
return connection
208+
# Fix https://github.com/jayvynl/django-clickhouse-backend/issues/53
209+
with self._connection_sharing_lock:
210+
if self.alias in self._clickhouse_connections:
211+
params, conn = self._clickhouse_connections[self.alias]
212+
if conn_params == params:
213+
return conn
214+
215+
conn = Database.connect(**conn_params)
216+
self._clickhouse_connections[self.alias] = (conn_params, conn)
217+
218+
return conn
205219

206220
def init_connection_state(self):
207221
pass
@@ -222,6 +236,13 @@ def _savepoint_commit(self, sid):
222236
def _set_autocommit(self, autocommit):
223237
pass
224238

239+
def _close(self):
240+
"""Close database connection.
241+
242+
This is a noop, because inner connection is shared between threads.
243+
"""
244+
pass
245+
225246
def is_usable(self):
226247
try:
227248
# Use a clickhouse_driver cursor directly, bypassing Django's utilities.

tests/backends/tests.py

+13-11
Original file line numberDiff line numberDiff line change
@@ -259,11 +259,7 @@ def create_squares(self, args, paramstyle, multiple):
259259
if paramstyle == "format":
260260
query = "INSERT INTO %s (%s, %s) VALUES" % (tbl, f1, f2)
261261
elif paramstyle == "pyformat":
262-
query = "INSERT INTO %s (%s, %s) VALUES" % (
263-
tbl,
264-
f1,
265-
f2,
266-
)
262+
query = "INSERT INTO %s (%s, %s) VALUES" % (tbl, f1, f2)
267263
else:
268264
raise ValueError("unsupported paramstyle in test")
269265
with connection.cursor() as cursor:
@@ -369,7 +365,7 @@ def test_unicode_password(self):
369365
self.fail("Unexpected error raised with Unicode password: %s" % e)
370366
finally:
371367
connection.settings_dict["PASSWORD"] = old_password
372-
connection.connection.close()
368+
connection.close()
373369
connection.connection = None
374370

375371
def test_database_operations_helper_class(self):
@@ -431,11 +427,10 @@ def test_is_usable_after_database_disconnects(self):
431427
# Open a connection to the database.
432428
with connection.cursor():
433429
pass
434-
# Emulate a connection close by the database.
430+
# _close is a noop, because underlying connection is shared between threads.
435431
connection._close()
436-
# Even then is_usable() should not raise an exception.
437432
try:
438-
self.assertFalse(connection.is_usable())
433+
self.assertTrue(connection.is_usable())
439434
finally:
440435
# Clean up the mess created by connection._close(). Since the
441436
# connection is already closed, this crashes on some backends.
@@ -705,6 +700,7 @@ def test_check_constraints_sql_keywords(self):
705700

706701
class ThreadTests(TransactionTestCase):
707702
available_apps = ["backends"]
703+
databases = {"default", "s1r2", "s2r1"}
708704

709705
def test_default_connection_thread_local(self):
710706
"""
@@ -736,15 +732,17 @@ def runner():
736732
t = threading.Thread(target=runner)
737733
t.start()
738734
t.join()
739-
# Each created connection got different inner connection.
735+
# The inner connection is shared between threads.
740736
self.assertEqual(
741-
len({conn.connection for conn in connections_dict.values()}), 3
737+
len({conn.connection for conn in connections_dict.values()}), 1
742738
)
743739
finally:
744740
# Finish by closing the connections opened by the other threads
745741
# (the connection opened in the main thread will automatically be
746742
# closed on teardown).
747743
for conn in connections_dict.values():
744+
# Regression test for https://github.com/jayvynl/django-clickhouse-backend/issues/53
745+
self.assertIs(conn.connection, connection.connection)
748746
if conn is not connection and conn.allow_thread_sharing:
749747
conn.close()
750748
conn.dec_thread_sharing()
@@ -783,6 +781,10 @@ def runner():
783781
# (the connection opened in the main thread will automatically be
784782
# closed on teardown).
785783
for conn in connections_dict.values():
784+
# Regression test for https://github.com/jayvynl/django-clickhouse-backend/issues/53
785+
if conn.vendor == "clickhouse":
786+
conn.ensure_connection()
787+
self.assertIs(conn.connection, connections[conn.alias].connection)
786788
if conn is not connection and conn.allow_thread_sharing:
787789
conn.close()
788790
conn.dec_thread_sharing()

0 commit comments

Comments
 (0)