Merge pull request #25 from Aignosi/fix/hotfix-opc-connection-optimizing

Refactor OPC server integrity checks to be asynchronous
This commit is contained in:
Matheus Demoner
2025-10-23 12:01:42 -03:00
committed by GitHub
5 changed files with 31 additions and 11 deletions

View File

@@ -414,7 +414,7 @@ class Ingestor:
# Check OPC cycles # Check OPC cycles
self.logger.info('Checking OPC servers integrity...') self.logger.info('Checking OPC servers integrity...')
self.ingestor_manager.check_opc_servers_integrity() await self.ingestor_manager.check_opc_servers_integrity()
self.logger.info('Updating managed tags...') self.logger.info('Updating managed tags...')
await self.update_ingestor_manager(current_managed_tags) await self.update_ingestor_manager(current_managed_tags)

View File

@@ -189,7 +189,7 @@ class IngestorManager(BaseActivity):
def __del__(self): def __del__(self):
asyncio.run(self.shutdown()) asyncio.run(self.shutdown())
async def update_opc_servers(self): async def update_opc_servers(self): # NOSONAR
""" """
Updates the OPC (OLE for Process Control) server connections managed by the ingestor. Updates the OPC (OLE for Process Control) server connections managed by the ingestor.
This method ensures that the OPC servers defined in `self.managed_tags` are properly This method ensures that the OPC servers defined in `self.managed_tags` are properly
@@ -228,7 +228,7 @@ class IngestorManager(BaseActivity):
elif server_instance.config != server_config: elif server_instance.config != server_config:
self.logger.warning(f'Reinitializing OPC manager for server {server}') self.logger.warning(f'Reinitializing OPC manager for server {server}')
server_instance.disconnect() await server_instance.shutdown()
del self.opc_managers[server] del self.opc_managers[server]
server_instance = await self.initialize_opc_from_config(server_config) server_instance = await self.initialize_opc_from_config(server_config)
else: else:
@@ -243,10 +243,12 @@ class IngestorManager(BaseActivity):
f'Failed to initialize OPC manager for server {server}, ' f'Failed to initialize OPC manager for server {server}, '
f'removing server from managed tags.' f'removing server from managed tags.'
) )
_a = [
for slot, _value in current_managed_tags.items():
if server in self.opc_managers:
await self.opc_managers[server].shutdown()
del self.opc_managers[server]
self.managed_tags[slot].pop(server, None) self.managed_tags[slot].pop(server, None)
for slot, _value in current_managed_tags.items()
]
servers = list(self.opc_managers.keys()) servers = list(self.opc_managers.keys())
for server in servers: for server in servers:
@@ -254,12 +256,12 @@ class IngestorManager(BaseActivity):
self.logger.warning( self.logger.warning(
f'Server {server} not found in managed tags. Desconnecting from server.' f'Server {server} not found in managed tags. Desconnecting from server.'
) )
await self.opc_managers[server].disconnect() await self.opc_managers[server].shutdown()
self.opc_managers.pop(server, None) self.opc_managers.pop(server, None)
metrics.OPC_MANAGERS_ACTIVE.labels(pod_id=self.pod_id).set(len(self.opc_managers)) metrics.OPC_MANAGERS_ACTIVE.labels(pod_id=self.pod_id).set(len(self.opc_managers))
def check_opc_servers_integrity(self): async def check_opc_servers_integrity(self):
""" """
Checks the integrity of the OPC servers and updates the OPC servers if necessary. Checks the integrity of the OPC servers and updates the OPC servers if necessary.
@@ -282,6 +284,9 @@ class IngestorManager(BaseActivity):
self.logger.warning(f'OPC server {server} is lost. Server will be disconnected.') self.logger.warning(f'OPC server {server} is lost. Server will be disconnected.')
for slot, _config in self.managed_tags.items(): for slot, _config in self.managed_tags.items():
if server in self.opc_managers:
await self.opc_managers[server].shutdown()
del self.opc_managers[server]
self.managed_tags[slot].pop(server, None) self.managed_tags[slot].pop(server, None)
metrics.OPC_MANAGERS_ACTIVE.labels(pod_id=self.pod_id).set(len(self.opc_managers)) metrics.OPC_MANAGERS_ACTIVE.labels(pod_id=self.pod_id).set(len(self.opc_managers))
@@ -604,4 +609,7 @@ class IngestorManager(BaseActivity):
to_remove.append([slot, server]) to_remove.append([slot, server])
for slot, server in to_remove: for slot, server in to_remove:
if server in self.opc_managers:
await self.opc_managers[server].shutdown()
del self.opc_managers[server]
self.managed_tags[slot].pop(server, None) self.managed_tags[slot].pop(server, None)

View File

@@ -107,6 +107,9 @@ class OpcManager(BaseActivity):
f'nodes={self.nodes}, subscriptions={self.subscriptions}' f'nodes={self.nodes}, subscriptions={self.subscriptions}'
) )
def __del__(self):
asyncio.run(self.shutdown())
async def shutdown(self): async def shutdown(self):
""" """
Comprehensive cleanup method for graceful shutdown. Comprehensive cleanup method for graceful shutdown.

View File

@@ -184,7 +184,7 @@ async def test_update_opc_servers(metrics, opc_manager, ingestor_manager):
'slot2': {'server3': {'config': 'config3'}, 'server1': {'config': 'config1'}}, 'slot2': {'server3': {'config': 'config3'}, 'server1': {'config': 'config1'}},
} }
mock = MagicMock(config={'config': 'old_config2'}) mock = AsyncMock(config={'config': 'old_config2'})
ingestor_manager.opc_managers['server3'] = AsyncMock(config={'config': 'config3'}) ingestor_manager.opc_managers['server3'] = AsyncMock(config={'config': 'config3'})
ingestor_manager.opc_managers['server2'] = mock ingestor_manager.opc_managers['server2'] = mock
ingestor_manager.opc_managers['server4'] = AsyncMock() ingestor_manager.opc_managers['server4'] = AsyncMock()
@@ -472,8 +472,9 @@ async def test_subscribe_to_tags(ingestor_manager):
ingestor_manager.managed_tags['slot1'].pop.assert_called_once_with('server3', None) ingestor_manager.managed_tags['slot1'].pop.assert_called_once_with('server3', None)
@mark.asyncio
@patch('ingestor.managers.ingestor_manager.metrics') @patch('ingestor.managers.ingestor_manager.metrics')
def test_check_opc_servers_integrity_all_healthy(metrics, ingestor_manager): async def test_check_opc_servers_integrity_all_healthy(metrics, ingestor_manager):
# Setup mock OPC managers # Setup mock OPC managers
opc_manager1 = MagicMock() opc_manager1 = MagicMock()
opc_manager1.check_cycles.return_value = None opc_manager1.check_cycles.return_value = None
@@ -491,7 +492,7 @@ def test_check_opc_servers_integrity_all_healthy(metrics, ingestor_manager):
ingestor_manager.initialize_opc_from_config = MagicMock() ingestor_manager.initialize_opc_from_config = MagicMock()
# Call the method # Call the method
ingestor_manager.check_opc_servers_integrity() await ingestor_manager.check_opc_servers_integrity()
# Verify that check_cycles and check_opc_listenning were called for each server # Verify that check_cycles and check_opc_listenning were called for each server
opc_manager1.check_cycles.assert_called_once() opc_manager1.check_cycles.assert_called_once()

View File

@@ -238,6 +238,7 @@ async def test_loop(ingestor_manager_started):
ingestor_manager_started.manage_no_slots = MagicMock() ingestor_manager_started.manage_no_slots = MagicMock()
ingestor_manager_started.manage_leases = AsyncMock() ingestor_manager_started.manage_leases = AsyncMock()
ingestor_manager_started.update_ingestor_manager = AsyncMock() ingestor_manager_started.update_ingestor_manager = AsyncMock()
ingestor_manager_started.ingestor_manager.check_opc_servers_integrity = AsyncMock()
ingestor_manager_started.ingestor_manager.managed_tags = { ingestor_manager_started.ingestor_manager.managed_tags = {
'slot1': 'server1', 'slot1': 'server1',
'slot2': 'server2', 'slot2': 'server2',
@@ -262,12 +263,16 @@ async def test_loop(ingestor_manager_started):
ingestor_manager_started.manage_leases.assert_called_once_with(4, 3, 2) ingestor_manager_started.manage_leases.assert_called_once_with(4, 3, 2)
ingestor_manager_started.ingestor_manager.update_slot_config.assert_called_once() ingestor_manager_started.ingestor_manager.update_slot_config.assert_called_once()
ingestor_manager_started.ingestor_manager.check_opc_servers_integrity.assert_called_once()
ingestor_manager_started.update_ingestor_manager.assert_called_once()
@mark.asyncio @mark.asyncio
async def test_loop_no_managed(ingestor_manager_started): async def test_loop_no_managed(ingestor_manager_started):
ingestor_manager_started.manage_no_slots = MagicMock() ingestor_manager_started.manage_no_slots = MagicMock()
ingestor_manager_started.manage_leases = AsyncMock() ingestor_manager_started.manage_leases = AsyncMock()
ingestor_manager_started.update_ingestor_manager = AsyncMock() ingestor_manager_started.update_ingestor_manager = AsyncMock()
ingestor_manager_started.ingestor_manager.check_opc_servers_integrity = AsyncMock()
ingestor_manager_started.ingestor_manager.managed_tags = {} ingestor_manager_started.ingestor_manager.managed_tags = {}
ingestor_manager_started.ingestor_manager.get_active_ingestors = MagicMock( ingestor_manager_started.ingestor_manager.get_active_ingestors = MagicMock(
return_value=['ingestor1', 'ingestor2'] return_value=['ingestor1', 'ingestor2']
@@ -288,6 +293,9 @@ async def test_loop_no_managed(ingestor_manager_started):
ingestor_manager_started.ingestor_manager.update_slot_config.assert_called_once() ingestor_manager_started.ingestor_manager.update_slot_config.assert_called_once()
ingestor_manager_started.logger.info.assert_any_call('No slots acquired in this loop') ingestor_manager_started.logger.info.assert_any_call('No slots acquired in this loop')
ingestor_manager_started.ingestor_manager.check_opc_servers_integrity.assert_called_once()
ingestor_manager_started.update_ingestor_manager.assert_called_once()
@mark.asyncio @mark.asyncio
async def test_loop_no_ingestor_manager(ingestor_manager_started): async def test_loop_no_ingestor_manager(ingestor_manager_started):