fix: Ensure only one Redis connection is created (#28930)

There is no gaurantee that setup_cache is only called once. This PR adds
a mutex lock to ensure only one thread gets to create the connection. If
both arrive at same time then one of them will be blocked until
connection is setup.

So far this hasn't been an issue because the "orphan" connection would
just get garbage collected but if you setup any kind of listener on it
or refer to it then it will keep running forever hurting performance.

This just has small performance impact on first request that sets up the
connection, in absence of contention the lock should have almost no
overhead. I make up for it by eliminating one function call :pinch:
This commit is contained in:
Ankush Menat 2024-12-27 12:55:32 +05:30 committed by GitHub
parent 6f7b4c412e
commit c4b8560247
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -18,6 +18,7 @@ import inspect
import json
import os
import sys
import threading
import traceback
import warnings
from collections import defaultdict
@ -272,7 +273,8 @@ def init(site: str, sites_path: str = ".", new_site: bool = False, force: bool =
local.dev_server = _dev_server
local.qb = get_query_builder(local.conf.db_type)
local.qb.get_query = get_query
setup_redis_cache_connection()
if not cache:
setup_redis_cache_connection()
if not _one_time_setup.get(local.conf.db_type):
patch_query_execute()
@ -490,14 +492,19 @@ def destroy():
release_local(local)
_redis_init_lock = threading.Lock()
def setup_redis_cache_connection():
"""Defines `frappe.cache` as `RedisWrapper` instance"""
from frappe.utils.redis_wrapper import setup_cache
global cache
if not cache:
from frappe.utils.redis_wrapper import setup_cache
cache = setup_cache()
with _redis_init_lock:
# We need to check again since someone else might have setup connection before us.
if not cache:
cache = setup_cache()
def get_traceback(with_context: bool = False) -> str: