Conversation
719f5b1 to
9f7104e
Compare
RobertKrawitz
left a comment
There was a problem hiding this comment.
The only substantive comments are about not making ResetSingletonLogin public (it's only used for testing) and the possibility of a race condition surrounding that. But you also need to make sure the tests run sequentially, not concurrently. Python (or at least CPython) is currently only single threaded, but if multi-threaded Python comes along and pytest allows concurrent test execution, you need to do what you can to ensure that that can't happen here.
Otherwise, minor changes.
|
|
||
| **mandatory:** KUBEADMIN_PASSWORD=$KUBEADMIN_PASSWORD | ||
|
|
||
| **mandatory:** KUBECONFIG_PATH=$KUBECONFIG_PATH [config path inside the container] |
There was a problem hiding this comment.
Add a comment that KUBECONFIG_PATH is no longer required and why
There was a problem hiding this comment.
I have added it recently due to 'oc login' issue, so its a new environment variable that is not required.
| from benchmark_runner.common.oc.oc_exceptions import LoginFailed | ||
|
|
||
|
|
||
| class SingletonLogin: |
| cls._instance = None | ||
|
|
||
| def __new__(cls, oc_instance): | ||
| if cls._instance is None: # First check (no locking) |
There was a problem hiding this comment.
I think there is a race condition here. Consider what happens if:
- cls._instance == instance1
- Someone calls new to create instance2
- The unprotected cls._instance is None returns false.
- instance1 calls reset_instance
- The new call now returns None rather than a new instance.
The only user of reset_instance I see here is the test code. Perhaps the test code should have a subclass inheriting from the singleton class that provides the reset method, so nothing else inadvertently calls it.
There was a problem hiding this comment.
There is only one place in the code for this login.
However, if it occurs, there is a locking line afterward, so there is no option to create two instances.
There was a problem hiding this comment.
The reset function provides a way to clear that login, hence the race condition.
What is the purpose of checking outside of the lock rather than just inside?
There was a problem hiding this comment.
We only lock only after getting instance: if cls._instance is None:
I dont think we should lock every instance
There was a problem hiding this comment.
Why not? What harm does it do? And the lock is only while getting the instance.
There was a problem hiding this comment.
Locking before checking, loos like this:
def __new__(cls, oc_instance):
with cls._lock: # Acquire the lock before checking
if cls._instance is None:
cls._instance = super(SingletonOCLogin, cls).__new__(cls)
cls._instance.__init_instance(oc_instance)
return cls._instance
The best practice is to check before locking.
It is not necessary to lock the instance before checking, as this may introduce a slight performance overhead.
The double-checked locking pattern is already optimal for ensuring thread safety while minimizing locking overhead.
There was a problem hiding this comment.
No, checking before locking (double-checked locking) is wrong unless there's an underlying guarantee of atomicity (such as the C/C++ volatile keyword). What happens if the lock is taken before checking and taking the lock (or worse yet, checking, another thread takes the lock, makes a change, and releases the lock with the protected object changed)? In this case, it would result in two calls to oc login.
Another thing that could go wrong is that the second thread might get a partially initialized object (what happens if the object is created and assigned before the object's init is called?).
Locking overhead is minimal in this case, given that at present only one thread will be running and that oc login takes much longer than taking and releasing the lock.
There was a problem hiding this comment.
According to the design pattern recommendation, the check should be done before acquiring the lock. For more details, read this article.
|
|
||
| **mandatory:** KUBEADMIN_PASSWORD=$KUBEADMIN_PASSWORD | ||
|
|
||
| **mandatory:** KUBECONFIG_PATH=$KUBECONFIG_PATH [config path inside the container] |
There was a problem hiding this comment.
Document that this has been removed and why.
There was a problem hiding this comment.
I have added it recently due to 'oc login' issue, so its a new environment variable that is not required.
|
|
||
| **optional:** KUBEADMIN_PASSWORD=$KUBEADMIN_PASSWORD | ||
|
|
||
| **mandatory:** KUBECONFIG_PATH=$KUBECONFIG_PATH [config path inside the container] |
There was a problem hiding this comment.
Document that this has been removed and why.
There was a problem hiding this comment.
I have added it recently due to 'oc login' issue, so its a new environment variable that is not required.
|
|
||
|
|
There was a problem hiding this comment.
Create the TestSingletonLogin (or TestSingletonOCLogin) class here. Maybe ResetSingletonOCLogin would be an even better name for the class since it's only being used for that purpose; everything else just uses SingletonLogin/SingletonOCLogin.
There was a problem hiding this comment.
I have changed the file name to test_singleton_oc_login.py.
No need to create a class TestSingletonOCLogin, I am using pytest and not unittest.
There was a problem hiding this comment.
It's to avoid having that dangerous reset member available. It's only needed for testing and should not be accessible outside of testing.
TestSingletonOCLogin is not correct, as pytest keys off the name. Perhaps ResetSingletonOCLogin would be a better name for it.
There was a problem hiding this comment.
I add the following fixture to pytest
@pytest.fixture(autouse=True)
def reset_singleton():
"""Fixture to reset SingletonOCLogin before each test."""
with SingletonOCLogin._lock:
SingletonOCLogin._instance = None
and delete reset_instance method from SingletonOCLogin class
@classmethod
def reset_instance(cls):
with cls._lock: # Acquire the lock for instance reset
cls._instance = None
RobertKrawitz
left a comment
There was a problem hiding this comment.
See previous comment for requested changes.
0132c12 to
80124fd
Compare
| if cls._instance is None: # First check (no locking) | ||
| with cls._lock: # Acquire the lock for instance creation | ||
| if cls._instance is None: # Second check (with locking) | ||
| cls._instance = super(SingletonOCLogin, cls).__new__(cls) | ||
| cls._instance.__init_instance(oc_instance) |
There was a problem hiding this comment.
You need to remove the double-checked lock. Period.
80124fd to
887f5e5
Compare
887f5e5 to
8746f40
Compare
Type of change
Note: Fill x in []
Description
Currently, the login method is placed under the OC class, and each OC instance creation triggers a login call.
I have built a dedicated Singleton Login class to ensure that the login is called only once per run.
Additionally, I have removed the kubeconfig_path variable because the API server URL is not needed when running oc login.
Adding relevant test cases for SingletonLogin class.
For security reasons, all pull requests need to be approved first before running any automated CI