Skip to content

fix(database): use issubclass instead of isinstance for pool class validation#10536

Closed
yashwantbezawada wants to merge 8 commits into
langflow-ai:mainfrom
yashwantbezawada:fix/pool-class-validation
Closed

fix(database): use issubclass instead of isinstance for pool class validation#10536
yashwantbezawada wants to merge 8 commits into
langflow-ai:mainfrom
yashwantbezawada:fix/pool-class-validation

Conversation

@yashwantbezawada
Copy link
Copy Markdown

@yashwantbezawada yashwantbezawada commented Nov 7, 2025

Description

Fixes #10231

This PR fixes a bug where database initialization crashes when users configure custom SQLAlchemy pool classes like NullPool via LANGFLOW_DB_CONNECTION_SETTINGS.

Problem

The code was using isinstance(pool_class(), sa.pool.Pool) to validate pool classes, which attempts to instantiate the pool class without required arguments. SQLAlchemy pool classes like NullPool, StaticPool, etc. require a creator argument during instantiation, causing this error:

Pool.__init__() missing 1 required positional argument: 'creator'

Solution

Changed to use issubclass(pool_class, sa.pool.Pool) which validates class inheritance without instantiation. This is the correct and standard way to check if a class is a subclass of another class in Python.

Changes

  • File: src/backend/base/langflow/services/database/service.py

    • Line 131: Changed isinstance(pool_class(), ...) to issubclass(pool_class, ...)
  • New File: src/backend/tests/unit/services/database/test_database_service.py

    • Added comprehensive test coverage for pool class validation
    • Tests for NullPool, StaticPool, QueuePool, and invalid pool class scenarios
    • Ensures no instantiation errors occur during validation

Testing

The new test file includes 4 test cases:

  1. test_null_pool_class_validation - Verifies NullPool can be validated without errors
  2. test_static_pool_class_validation - Verifies StaticPool can be validated without errors
  3. test_invalid_pool_class_logs_error - Ensures invalid pool classes log errors gracefully
  4. test_queue_pool_class_validation - Verifies QueuePool (default) works correctly

Impact

  • Fixes PostgreSQL users with custom pool configurations
  • No breaking changes - maintains existing behavior
  • Improves code correctness by using proper Python idiom

Summary by CodeRabbit

  • Bug Fixes

    • Improved database pool class validation logic to ensure proper error handling and fallback behavior.
  • Tests

    • Added comprehensive unit tests for database pool class validation scenarios, including valid and invalid configurations.

…lidation

Fixes langflow-ai#10231

The pool class validation was incorrectly using isinstance(pool_class(), ...)
which attempts to instantiate the pool class without required arguments.
SQLAlchemy pool classes like NullPool require a 'creator' argument during
instantiation, causing crashes when users configure custom pool classes.

Changed to use issubclass(pool_class, ...) which validates class inheritance
without instantiation. This is the correct way to check if a class is a
subclass of sa.pool.Pool.

Also added comprehensive test coverage for pool class validation including
NullPool, StaticPool, QueuePool, and invalid pool class scenarios.
@github-actions github-actions Bot added the community Pull Request from an external contributor label Nov 7, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 7, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The change modifies SQLAlchemy pool class validation logic in DatabaseService initialization from attempting instantiation via isinstance(pool_class(), sa.pool.Pool) to using subclass checking via issubclass(pool_class, sa.pool.Pool). This resolves a bug where pool classes requiring initialization arguments fail during validation. Corresponding unit tests validate behavior for multiple pool types and error cases.

Changes

Cohort / File(s) Summary
Pool Class Validation Logic
src/backend/base/langflow/services/database/service.py
Changed pool class validation from instantiation-based isinstance() check to issubclass() check to avoid attempting instantiation of pool classes that require constructor arguments (e.g., creator).
Test Coverage for Pool Validation
src/backend/tests/unit/services/database/test_database_service.py
Added TestPoolClassValidation test class with four test methods validating NullPool, StaticPool, QueuePool, and invalid pool class handling; tests verify correct pool class is passed to create_async_engine and error logging occurs for invalid classes.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

  • Area of focus: Verify that issubclass() correctly validates all SQLAlchemy pool class variants (NullPool, StaticPool, QueuePool, etc.)
  • Area of focus: Confirm error handling and logging behavior remains intact when invalid pool class names are provided via settings
  • Area of focus: Ensure test mocking strategy accurately reflects the service initialization flow

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Test Quality And Coverage ⚠️ Warning Test coverage exists for valid pool classes (NullPool, StaticPool, QueuePool) and invalid class error logging, but lacks explicit verification that pool classes are NOT instantiated and missing edge case testing. Add tests verifying pool classes aren't instantiated, test None/missing poolclass cases, add TypeError handling verification, and remove unused pytest import.
Test File Naming And Structure ⚠️ Warning Test file has unused pytest import, lacks proper fixtures for setup/teardown, missing comprehensive edge case coverage, and insufficient test documentation. Remove unused pytest import, add pytest fixtures for setup/teardown, expand edge case tests (None values, non-class objects), and add docstrings to test functions explaining their purpose.
✅ Passed checks (7 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: switching from isinstance to issubclass for pool class validation, which is the core fix in the PR.
Linked Issues check ✅ Passed The PR addresses issue #10231 by replacing isinstance(pool_class(), sa.pool.Pool) with issubclass(pool_class, sa.pool.Pool) to fix pool initialization errors without instantiation.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing pool class validation: modifying the validation logic in service.py and adding comprehensive tests for the fix.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Test Coverage For New Implementations ✅ Passed PR includes comprehensive test coverage with 4 test functions covering valid pool classes (NullPool, StaticPool, QueuePool) and invalid class scenario using proper mocking and assertions.
Excessive Mock Usage Warning ✅ Passed The test file uses mocks appropriately for external dependencies (create_async_engine, settings, logger) while testing actual validation logic with real SQLAlchemy pool objects. Mock-to-code ratio is reasonable and not excessive.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added the bug Something isn't working label Nov 7, 2025
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Nov 7, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/backend/base/langflow/services/database/service.py (1)

131-135: Remove invalid poolclass from kwargs when validation fails.

When poolclass validation fails (line 135), the error is logged but the invalid string value remains in kwargs and gets passed to create_async_engine on line 137. SQLAlchemy expects poolclass to be a class object, not a string, so this will cause a downstream error.

Apply this diff to remove the invalid poolclass:

             if pool_class and isinstance(pool_class, type) and issubclass(pool_class, sa.pool.Pool):
                 logger.debug(f"Using poolclass: {poolclass_key}.")
                 kwargs["poolclass"] = pool_class
             else:
                 logger.error(f"Invalid poolclass '{poolclass_key}' specified. Using default pool class.")
+                kwargs.pop("poolclass", None)
🧹 Nitpick comments (1)
src/backend/tests/unit/services/database/test_database_service.py (1)

28-28: Suppress or avoid unused variable warnings.

The db_service variable is assigned in all four tests to trigger the __init__ validation logic but is never referenced afterward, causing linter warnings (F841).

Consider one of these approaches:

Option 1: Use underscore to indicate intentional non-use:

-        db_service = DatabaseService(mock_settings_service)
+        _ = DatabaseService(mock_settings_service)

Option 2: Add noqa comment:

-        db_service = DatabaseService(mock_settings_service)
+        db_service = DatabaseService(mock_settings_service)  # noqa: F841

Also applies to: 49-49, 71-71, 98-98

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 403b939 and f072d12.

📒 Files selected for processing (2)
  • src/backend/base/langflow/services/database/service.py (1 hunks)
  • src/backend/tests/unit/services/database/test_database_service.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
{src/backend/**/*.py,tests/**/*.py,Makefile}

📄 CodeRabbit inference engine (.cursor/rules/backend_development.mdc)

{src/backend/**/*.py,tests/**/*.py,Makefile}: Run make format_backend to format Python code before linting or committing changes
Run make lint to perform linting checks on backend Python code

Files:

  • src/backend/base/langflow/services/database/service.py
  • src/backend/tests/unit/services/database/test_database_service.py
src/backend/tests/unit/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/backend_development.mdc)

Test component integration within flows using create_flow, build_flow, and get_build_events utilities

Files:

  • src/backend/tests/unit/services/database/test_database_service.py
src/backend/tests/**/*.py

📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)

src/backend/tests/**/*.py: Unit tests for backend code must be located in the 'src/backend/tests/' directory, with component tests organized by component subdirectory under 'src/backend/tests/unit/components/'.
Test files should use the same filename as the component under test, with an appropriate test prefix or suffix (e.g., 'my_component.py' → 'test_my_component.py').
Use the 'client' fixture (an async httpx.AsyncClient) for API tests in backend Python tests, as defined in 'src/backend/tests/conftest.py'.
When writing component tests, inherit from the appropriate base class in 'src/backend/tests/base.py' (ComponentTestBase, ComponentTestBaseWithClient, or ComponentTestBaseWithoutClient) and provide the required fixtures: 'component_class', 'default_kwargs', and 'file_names_mapping'.
Each test in backend Python test files should have a clear docstring explaining its purpose, and complex setups or mocks should be well-commented.
Test both sync and async code paths in backend Python tests, using '@pytest.mark.asyncio' for async tests.
Mock external dependencies appropriately in backend Python tests to isolate unit tests from external services.
Test error handling and edge cases in backend Python tests, including using 'pytest.raises' and asserting error messages.
Validate input/output behavior and test component initialization and configuration in backend Python tests.
Use the 'no_blockbuster' pytest marker to skip the blockbuster plugin in tests when necessary.
Be aware of ContextVar propagation in async tests; test both direct event loop execution and 'asyncio.to_thread' scenarios to ensure proper context isolation.
Test error handling by mocking internal functions using monkeypatch in backend Python tests.
Test resource cleanup in backend Python tests by using fixtures that ensure proper initialization and cleanup of resources.
Test timeout and performance constraints in backend Python tests using 'asyncio.wait_for' and timing assertions.
Test Langflow's Messag...

Files:

  • src/backend/tests/unit/services/database/test_database_service.py
🧠 Learnings (3)
📚 Learning: 2025-07-21T14:16:14.125Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-21T14:16:14.125Z
Learning: Applies to src/backend/tests/**/*.py : Mock external dependencies appropriately in backend Python tests to isolate unit tests from external services.

Applied to files:

  • src/backend/tests/unit/services/database/test_database_service.py
📚 Learning: 2025-07-21T14:16:14.125Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-21T14:16:14.125Z
Learning: Applies to src/backend/tests/unit/test_database.py : For database-related tests that may fail in batch runs but pass individually, consider running them sequentially and be aware of this behavior when writing such tests.

Applied to files:

  • src/backend/tests/unit/services/database/test_database_service.py
📚 Learning: 2025-07-21T14:16:14.125Z
Learnt from: CR
Repo: langflow-ai/langflow PR: 0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-21T14:16:14.125Z
Learning: Applies to src/backend/tests/**/*.py : Test error handling by mocking internal functions using monkeypatch in backend Python tests.

Applied to files:

  • src/backend/tests/unit/services/database/test_database_service.py
🧬 Code graph analysis (1)
src/backend/tests/unit/services/database/test_database_service.py (1)
src/backend/base/langflow/services/database/service.py (1)
  • DatabaseService (39-485)
🪛 GitHub Actions: Ruff Style Check
src/backend/tests/unit/services/database/test_database_service.py

[error] 5-5: Ruff: F401 'pytest' imported but unused.

🪛 GitHub Check: Ruff Style Check (3.13)
src/backend/tests/unit/services/database/test_database_service.py

[failure] 98-98: Ruff (F841)
src/backend/tests/unit/services/database/test_database_service.py:98:9: F841 Local variable db_service is assigned to but never used


[failure] 71-71: Ruff (F841)
src/backend/tests/unit/services/database/test_database_service.py:71:9: F841 Local variable db_service is assigned to but never used


[failure] 49-49: Ruff (F841)
src/backend/tests/unit/services/database/test_database_service.py:49:9: F841 Local variable db_service is assigned to but never used


[failure] 28-28: Ruff (F841)
src/backend/tests/unit/services/database/test_database_service.py:28:9: F841 Local variable db_service is assigned to but never used


[failure] 5-5: Ruff (F401)
src/backend/tests/unit/services/database/test_database_service.py:5:8: F401 pytest imported but unused

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: Lint Backend / Run Mypy (3.12)
  • GitHub Check: Run Backend Tests / LFX Tests - Python 3.10
  • GitHub Check: Run Frontend Tests / Determine Test Suites and Shard Distribution
  • GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 3
  • GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 5
  • GitHub Check: Run Backend Tests / Integration Tests - Python 3.10
  • GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 2
  • GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 4
  • GitHub Check: Run Backend Tests / Unit Tests - Python 3.10 - Group 1
  • GitHub Check: Test Starter Templates
  • GitHub Check: Optimize new Python code in this PR
  • GitHub Check: Update Component Index
🔇 Additional comments (1)
src/backend/tests/unit/services/database/test_database_service.py (1)

79-83: Test should verify that invalid poolclass is removed from kwargs.

Lines 82-83 check whether the invalid poolclass string is still present in kwargs, which documents the current buggy behavior where invalid strings are passed to create_async_engine. Once the bug in service.py is fixed (by removing invalid poolclass from kwargs), this test assertion should be updated to verify that poolclass is not in call_kwargs.

After applying the fix in service.py to remove invalid poolclass from kwargs, update this test:

         # Verify that poolclass was NOT passed to create_async_engine
         call_kwargs = mock_create_engine.call_args[1]
-        # The invalid poolclass should not be in the kwargs, or if it is, it should be the string not a class
-        if "poolclass" in call_kwargs:
-            assert call_kwargs["poolclass"] == "InvalidPoolClass"
+        # The invalid poolclass should have been removed from kwargs
+        assert "poolclass" not in call_kwargs

if poolclass_key is not None:
pool_class = getattr(sa, poolclass_key, None)
if pool_class and isinstance(pool_class(), sa.pool.Pool):
if pool_class and issubclass(pool_class, sa.pool.Pool):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Good fix, but add TypeError protection.

The change from isinstance(pool_class(), sa.pool.Pool) to issubclass(pool_class, sa.pool.Pool) correctly fixes the instantiation bug without requiring pool constructor arguments.

However, issubclass() raises TypeError if the first argument is not a class. Since getattr(sa, poolclass_key, None) on line 130 could return non-class attributes from the sa module (functions, constants, etc.), you should add a type check.

Apply this diff to prevent TypeError:

-            if pool_class and issubclass(pool_class, sa.pool.Pool):
+            if pool_class and isinstance(pool_class, type) and issubclass(pool_class, sa.pool.Pool):
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if pool_class and issubclass(pool_class, sa.pool.Pool):
if pool_class and isinstance(pool_class, type) and issubclass(pool_class, sa.pool.Pool):
🤖 Prompt for AI Agents
In src/backend/base/langflow/services/database/service.py around line 131,
issubclass(pool_class, sa.pool.Pool) can raise TypeError if pool_class is not a
class; check that pool_class is a class (e.g., using isinstance(pool_class,
type) or inspect.isclass(pool_class)) before calling issubclass, and only call
issubclass when that check passes so non-class attributes from getattr(sa,
poolclass_key, None) are ignored.


from unittest.mock import MagicMock, patch

import pytest
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused pytest import.

The pytest import is not used anywhere in this test file.

Apply this diff:

-import pytest
 import sqlalchemy as sa
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import pytest
import sqlalchemy as sa
🧰 Tools
🪛 GitHub Actions: Ruff Style Check

[error] 5-5: Ruff: F401 'pytest' imported but unused.

🪛 GitHub Check: Ruff Style Check (3.13)

[failure] 5-5: Ruff (F401)
src/backend/tests/unit/services/database/test_database_service.py:5:8: F401 pytest imported but unused

🤖 Prompt for AI Agents
In src/backend/tests/unit/services/database/test_database_service.py around line
5, the file imports pytest but never uses it; remove the unused "import pytest"
line to clean up imports and avoid linter warnings.

…strings

- Add pytest fixture for common mock setup
- Add edge case tests for None, missing, and non-string poolclass values
- Add comprehensive docstrings explaining test purpose and context
- Add explicit verification that classes (not instances) are passed to engine
- Improve test organization and readability
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Nov 7, 2025
- Add isinstance(pool_class, type) check before issubclass to prevent TypeError
- Restore pytest import needed for @pytest.fixture decorator
- Addresses CodeRabbit feedback on line 131 and test file imports
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Nov 7, 2025
- Add assertions for db_service to verify successful initialization
- Use mock_logger and mock_create_engine in test_non_string_pool_class_value
- Fixes F841 (unused variable) and ARG002 (unused argument) errors
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Nov 7, 2025
- Add kwargs.pop("poolclass", None) when validation fails
- Update test to verify invalid poolclass is removed from kwargs
- Addresses CodeRabbit feedback about downstream SQLAlchemy errors
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Nov 7, 2025
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Nov 7, 2025
Fixes TypeError when non-string values are passed as poolclass.
The test test_non_string_pool_class_value was failing because getattr()
requires a string attribute name, but the code didn't validate the type
before calling getattr(). This adds an isinstance() check to ensure
poolclass_key is a string before attempting to use it.

This addresses the CI failure in Unit Tests - Python 3.10 - Group 2.
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Nov 15, 2025
@yashwantbezawada
Copy link
Copy Markdown
Author

Closing this as the fix was already merged in #10232. The main branch now uses issubclass for pool class validation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working community Pull Request from an external contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect sqlachemy pool initialization

1 participant