-
Notifications
You must be signed in to change notification settings - Fork 26
RDBC-948: Ensure topology handling matches C# Client. #249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| from threading import Event | ||
| from ravendb.documents.store.definition import DocumentStore | ||
| from ravendb.documents.subscriptions.options import SubscriptionWorkerOptions | ||
| from ravendb.exceptions.exceptions import AllTopologyNodesDownException | ||
| from ravendb.infrastructure.entities import User | ||
| from ravendb.serverwide.operations.common import GetDatabaseRecordOperation | ||
| from ravendb.tests.test_base import TestBase | ||
| from ravendb.http.topology import UpdateTopologyParameters | ||
| from ravendb.http.server_node import ServerNode | ||
|
|
||
|
|
||
| class TestRDBC948(TestBase): | ||
| def setUp(self): | ||
| super().setUp() | ||
|
|
||
| def test_failover_with_invalid_dns_in_urls(self): | ||
| # One invalid DNS hostname and one valid server URL (from the embedded test server) | ||
| invalid_host = "http://thisnamedoesnotexist:8080" | ||
| valid_url = self.store.urls[0] | ||
|
|
||
| with DocumentStore(urls=[invalid_host, valid_url], database=self.store.database) as store2: | ||
| store2.conventions.disable_topology_updates = False | ||
| store2.initialize() | ||
|
|
||
| # Should succeed by failing over to the valid URL | ||
| with store2.open_session() as session: | ||
| session.store({"Name": "John"}, "users/1") | ||
| session.save_changes() | ||
|
|
||
| # Verify we can read it back (continues using the healthy node) | ||
| with store2.open_session() as session: | ||
| doc = session.load("users/1") | ||
| self.assertIsNotNone(doc) | ||
| self.assertEqual(doc.get("Name"), "John") | ||
|
|
||
| def test_all_nodes_down_throws(self): | ||
| # Two unreachable endpoints: invalid DNS and a closed localhost port | ||
| urls = [ | ||
| "http://thisnamedoesnotexist:8080", | ||
| "http://127.0.0.1:1234", | ||
| ] | ||
|
|
||
| with DocumentStore(urls=urls, database=self.store.database) as store2: | ||
| store2.conventions.disable_topology_updates = False | ||
| store2.initialize() | ||
|
|
||
| with self.assertRaises(AllTopologyNodesDownException): | ||
| with store2.open_session() as session: | ||
| session.load("users/does-not-matter") | ||
|
|
||
| def test_maintenance_operation_failover_with_invalid_dns(self): | ||
| invalid_host = "http://thisnamedoesnotexist:8080" | ||
| valid_url = self.store.urls[0] | ||
| database = self.store.database | ||
|
|
||
| with DocumentStore(urls=[invalid_host, valid_url], database=database) as store2: | ||
| store2.conventions.disable_topology_updates = False | ||
| store2.initialize() | ||
|
|
||
| # Perform maintenance call, should succeed by failing over | ||
| record = store2.maintenance.server.send(GetDatabaseRecordOperation(database)) | ||
| self.assertIsNotNone(record) | ||
|
|
||
| def test_request_executor_failover_with_invalid_dns(self): | ||
| invalid_host = "http://thisnamedoesnotexist:8080" | ||
| valid_url = self.store.urls[0] | ||
|
|
||
| with DocumentStore(urls=[invalid_host, valid_url], database=self.store.database) as store2: | ||
| store2.conventions.disable_topology_updates = False | ||
| store2.initialize() | ||
|
|
||
| # Explicitly refresh topology like C# tests via UpdateTopologyAsync. | ||
| # This avoids racing the background first-topology-update and ensures the selector is initialized. | ||
| req_ex = store2.get_request_executor() | ||
| params = UpdateTopologyParameters(ServerNode(valid_url, store2.database)) | ||
| params.timeout_in_ms = 5 | ||
| params.debug_tag = "test-init" | ||
| req_ex.update_topology_async(params).result() | ||
| # Now URL should reflect the healthy node | ||
| self.assertIsNotNone(req_ex.url, "request executor URL did not initialize") | ||
| self.assertTrue(req_ex.url.startswith(valid_url), f"unexpected URL: {req_ex.url}") | ||
|
|
||
| # And simple operations should succeed | ||
| with store2.open_session() as session: | ||
| session.store({"Name": "Jane"}, "users/2") | ||
| session.save_changes() | ||
|
|
||
| def test_subscription_failover_with_invalid_dns(self): | ||
| invalid_host = "http://thisnamedoesnotexist:8080" | ||
| valid_url = self.store.urls[0] | ||
|
|
||
| with DocumentStore(urls=[invalid_host, valid_url], database=self.store.database) as store2: | ||
| store2.conventions.disable_topology_updates = False | ||
| store2.initialize() | ||
|
|
||
| # Create a subscription and ensure worker connects and receives items | ||
| sub_id = store2.subscriptions.create_for_class(User) | ||
| with store2.subscriptions.get_subscription_worker(SubscriptionWorkerOptions(sub_id), User) as worker: | ||
| got_item = Event() | ||
|
|
||
| def _run(batch): | ||
| for item in batch.items: | ||
| if item.result is not None: | ||
| got_item.set() | ||
|
|
||
| worker.run(_run) | ||
|
|
||
| # Add a document so the subscription has something to send | ||
| with store2.open_session() as session: | ||
| session.store(User(name="SubUser")) | ||
| session.save_changes() | ||
|
|
||
| self.assertTrue(got_item.wait(10)) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.