nebu 0.1.117__tar.gz → 0.1.119__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nebu-0.1.117/src/nebu.egg-info → nebu-0.1.119}/PKG-INFO +1 -1
- {nebu-0.1.117 → nebu-0.1.119}/pyproject.toml +1 -1
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/processors/consumer.py +197 -20
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/processors/consumer_health_worker.py +147 -6
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/processors/models.py +6 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/processors/processor.py +53 -30
- {nebu-0.1.117 → nebu-0.1.119/src/nebu.egg-info}/PKG-INFO +1 -1
- {nebu-0.1.117 → nebu-0.1.119}/LICENSE +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/README.md +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/setup.cfg +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/__init__.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/auth.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/builders/builder.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/builders/models.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/cache.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/config.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/containers/container.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/containers/models.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/data.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/errors.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/logging.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/meta.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/namespaces/models.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/namespaces/namespace.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/orign.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/processors/consumer_process_worker.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/processors/decorate.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/processors/default.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/redis/models.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu/services/service.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu.egg-info/SOURCES.txt +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu.egg-info/dependency_links.txt +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu.egg-info/requires.txt +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/src/nebu.egg-info/top_level.txt +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/tests/test_bucket.py +0 -0
- {nebu-0.1.117 → nebu-0.1.119}/tests/test_containers.py +0 -0
@@ -388,7 +388,13 @@ def start_health_check_subprocess() -> Optional[subprocess.Popen]:
|
|
388
388
|
"""Start the health check consumer subprocess."""
|
389
389
|
global REDIS_HEALTH_STREAM, REDIS_HEALTH_CONSUMER_GROUP
|
390
390
|
|
391
|
+
print(f"[DEBUG] start_health_check_subprocess called")
|
392
|
+
print(f"[DEBUG] REDIS_URL: {REDIS_URL}")
|
393
|
+
print(f"[DEBUG] REDIS_HEALTH_STREAM: {REDIS_HEALTH_STREAM}")
|
394
|
+
print(f"[DEBUG] REDIS_HEALTH_CONSUMER_GROUP: {REDIS_HEALTH_CONSUMER_GROUP}")
|
395
|
+
|
391
396
|
if not all([REDIS_URL, REDIS_HEALTH_STREAM, REDIS_HEALTH_CONSUMER_GROUP]):
|
397
|
+
print(f"[DEBUG] Health check not configured - missing required variables")
|
392
398
|
logger.warning(
|
393
399
|
"[Consumer] Health check stream not configured. Health consumer subprocess not started."
|
394
400
|
)
|
@@ -399,6 +405,11 @@ def start_health_check_subprocess() -> Optional[subprocess.Popen]:
|
|
399
405
|
assert isinstance(REDIS_HEALTH_STREAM, str)
|
400
406
|
assert isinstance(REDIS_HEALTH_CONSUMER_GROUP, str)
|
401
407
|
|
408
|
+
print(
|
409
|
+
f"[DEBUG] Starting health check subprocess for stream: {REDIS_HEALTH_STREAM}"
|
410
|
+
)
|
411
|
+
print(f"[DEBUG] Health consumer group: {REDIS_HEALTH_CONSUMER_GROUP}")
|
412
|
+
|
402
413
|
# Prepare environment variables for the subprocess
|
403
414
|
health_env = os.environ.copy()
|
404
415
|
health_env["REDIS_HEALTH_STREAM"] = REDIS_HEALTH_STREAM
|
@@ -412,6 +423,8 @@ def start_health_check_subprocess() -> Optional[subprocess.Popen]:
|
|
412
423
|
"nebu.processors.consumer_health_worker",
|
413
424
|
]
|
414
425
|
|
426
|
+
print(f"[DEBUG] Health subprocess command: {' '.join(health_cmd)}")
|
427
|
+
|
415
428
|
process = subprocess.Popen(
|
416
429
|
health_cmd,
|
417
430
|
stdout=subprocess.PIPE,
|
@@ -422,12 +435,17 @@ def start_health_check_subprocess() -> Optional[subprocess.Popen]:
|
|
422
435
|
bufsize=1, # Line buffered
|
423
436
|
)
|
424
437
|
|
438
|
+
print(
|
439
|
+
f"[DEBUG] Health check subprocess started successfully with PID {process.pid}"
|
440
|
+
)
|
425
441
|
logger.info(
|
426
442
|
f"[Consumer] Health check subprocess started with PID {process.pid}"
|
427
443
|
)
|
428
444
|
return process
|
429
445
|
|
430
446
|
except Exception as e:
|
447
|
+
print(f"[DEBUG] Failed to start health check subprocess: {e}")
|
448
|
+
print(f"[DEBUG] Exception type: {type(e)}")
|
431
449
|
logger.error(f"[Consumer] Failed to start health check subprocess: {e}")
|
432
450
|
logger.exception("Health Subprocess Start Error Traceback:")
|
433
451
|
return None
|
@@ -435,13 +453,17 @@ def start_health_check_subprocess() -> Optional[subprocess.Popen]:
|
|
435
453
|
|
436
454
|
def monitor_health_subprocess(process: subprocess.Popen) -> None:
|
437
455
|
"""Monitor the health check subprocess and log its output."""
|
456
|
+
print(f"[DEBUG] monitor_health_subprocess started for PID {process.pid}")
|
438
457
|
try:
|
439
458
|
# Read output from the subprocess
|
440
459
|
if process.stdout:
|
441
460
|
for line in iter(process.stdout.readline, ""):
|
461
|
+
print(f"[DEBUG] [HealthSubprocess] {line.strip()}")
|
442
462
|
logger.info(f"[HealthSubprocess] {line.strip()}")
|
443
463
|
process.stdout.close() if process.stdout else None
|
464
|
+
print(f"[DEBUG] monitor_health_subprocess finished for PID {process.pid}")
|
444
465
|
except Exception as e:
|
466
|
+
print(f"[DEBUG] Error monitoring health subprocess: {e}")
|
445
467
|
logger.error(f"[Consumer] Error monitoring health subprocess: {e}")
|
446
468
|
|
447
469
|
|
@@ -449,33 +471,45 @@ def check_health_subprocess() -> bool:
|
|
449
471
|
"""Check if the health subprocess is still running and restart if needed."""
|
450
472
|
global health_subprocess
|
451
473
|
|
474
|
+
print(f"[DEBUG] check_health_subprocess called")
|
475
|
+
|
452
476
|
if health_subprocess is None:
|
477
|
+
print(f"[DEBUG] health_subprocess is None")
|
453
478
|
return False
|
454
479
|
|
455
480
|
# Check if process is still running
|
456
|
-
|
481
|
+
poll_result = health_subprocess.poll()
|
482
|
+
print(f"[DEBUG] health_subprocess.poll() returned: {poll_result}")
|
483
|
+
|
484
|
+
if poll_result is None:
|
485
|
+
print(f"[DEBUG] Health subprocess still running (PID {health_subprocess.pid})")
|
457
486
|
return True # Still running
|
458
487
|
|
459
488
|
# Process has exited
|
460
489
|
exit_code = health_subprocess.returncode
|
490
|
+
print(f"[DEBUG] Health subprocess exited with code {exit_code}")
|
461
491
|
logger.warning(
|
462
492
|
f"[Consumer] Health subprocess exited with code {exit_code}. Restarting..."
|
463
493
|
)
|
464
494
|
|
465
495
|
# Start a new health subprocess
|
496
|
+
print(f"[DEBUG] Attempting to restart health subprocess...")
|
466
497
|
health_subprocess = start_health_check_subprocess()
|
467
498
|
|
468
499
|
if health_subprocess:
|
500
|
+
print(f"[DEBUG] Health subprocess restarted successfully")
|
469
501
|
# Start monitoring thread for the new subprocess
|
470
502
|
monitor_thread = threading.Thread(
|
471
503
|
target=monitor_health_subprocess, args=(health_subprocess,), daemon=True
|
472
504
|
)
|
473
505
|
monitor_thread.start()
|
506
|
+
print(f"[DEBUG] Monitor thread started for health subprocess")
|
474
507
|
logger.info(
|
475
508
|
"[Consumer] Health subprocess restarted and monitoring thread started."
|
476
509
|
)
|
477
510
|
return True
|
478
511
|
else:
|
512
|
+
print(f"[DEBUG] Failed to restart health subprocess")
|
479
513
|
logger.error("[Consumer] Failed to restart health subprocess.")
|
480
514
|
return False
|
481
515
|
|
@@ -714,6 +748,9 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
|
|
714
748
|
user_id = None
|
715
749
|
try:
|
716
750
|
payload_str = message_data.get("data")
|
751
|
+
print(f"[DEBUG] Raw message_data: {message_data}")
|
752
|
+
print(f"[DEBUG] Payload string: {payload_str}")
|
753
|
+
|
717
754
|
if (
|
718
755
|
not payload_str
|
719
756
|
): # Covers None and empty string, isinstance check is redundant
|
@@ -722,6 +759,7 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
|
|
722
759
|
)
|
723
760
|
try:
|
724
761
|
raw_payload = json.loads(payload_str)
|
762
|
+
print(f"[DEBUG] Parsed raw_payload: {json.dumps(raw_payload, indent=2)}")
|
725
763
|
except json.JSONDecodeError as json_err:
|
726
764
|
raise ValueError(f"Failed to parse JSON payload: {json_err}") from json_err
|
727
765
|
if not isinstance(raw_payload, dict):
|
@@ -734,6 +772,9 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
|
|
734
772
|
# --- Extract fields from the *inner* message content for HealthCheck and regular processing ---
|
735
773
|
# The actual message content is inside raw_payload["content"]
|
736
774
|
inner_content_data = raw_payload.get("content", {})
|
775
|
+
print(
|
776
|
+
f"[DEBUG] Extracted inner_content_data: {json.dumps(inner_content_data, indent=2) if isinstance(inner_content_data, dict) else inner_content_data}"
|
777
|
+
)
|
737
778
|
|
738
779
|
# Add debug logging for content structure analysis
|
739
780
|
logger.debug(f">> inner_content_data type: {type(inner_content_data)}")
|
@@ -746,6 +787,7 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
|
|
746
787
|
# we can't reliably get 'kind' or other fields from it.
|
747
788
|
# This case is more relevant for non-StreamMessage processors.
|
748
789
|
# For HealthChecks, we expect a structured 'content'.
|
790
|
+
print(f"[DEBUG] inner_content_data is not a dict, using defaults")
|
749
791
|
logger.warning(
|
750
792
|
f"Received non-dict inner_content_data: {inner_content_data}. HealthCheck might be missed if applicable."
|
751
793
|
)
|
@@ -762,6 +804,8 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
|
|
762
804
|
and "content" in inner_content_data
|
763
805
|
)
|
764
806
|
|
807
|
+
print(f"[DEBUG] has_message_structure: {has_message_structure}")
|
808
|
+
print(f"[DEBUG] inner_content_data keys: {list(inner_content_data.keys())}")
|
765
809
|
logger.debug(f">> has_message_structure: {has_message_structure}")
|
766
810
|
|
767
811
|
if has_message_structure:
|
@@ -770,6 +814,13 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
|
|
770
814
|
inner_msg_id = inner_content_data.get("id", "")
|
771
815
|
actual_content_to_process = inner_content_data.get("content", {})
|
772
816
|
inner_created_at_str = inner_content_data.get("created_at")
|
817
|
+
print(f"[DEBUG] Using nested structure:")
|
818
|
+
print(f"[DEBUG] inner_kind: '{inner_kind}'")
|
819
|
+
print(f"[DEBUG] inner_msg_id: '{inner_msg_id}'")
|
820
|
+
print(
|
821
|
+
f"[DEBUG] actual_content_to_process: {actual_content_to_process}"
|
822
|
+
)
|
823
|
+
print(f"[DEBUG] inner_created_at_str: {inner_created_at_str}")
|
773
824
|
logger.debug(
|
774
825
|
f">> Using nested structure - inner_kind: {inner_kind}, inner_msg_id: {inner_msg_id}"
|
775
826
|
)
|
@@ -786,6 +837,13 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
|
|
786
837
|
inner_created_at_str = raw_payload.get(
|
787
838
|
"created_at"
|
788
839
|
) # Get created_at from outer payload
|
840
|
+
print(f"[DEBUG] Using direct structure:")
|
841
|
+
print(f"[DEBUG] inner_kind: '{inner_kind}' (from outer payload)")
|
842
|
+
print(f"[DEBUG] inner_msg_id: '{inner_msg_id}' (from outer payload)")
|
843
|
+
print(
|
844
|
+
f"[DEBUG] actual_content_to_process: {actual_content_to_process}"
|
845
|
+
)
|
846
|
+
print(f"[DEBUG] inner_created_at_str: {inner_created_at_str}")
|
789
847
|
logger.debug(
|
790
848
|
f">> Using direct structure - inner_kind: {inner_kind}, inner_msg_id: {inner_msg_id}"
|
791
849
|
)
|
@@ -810,31 +868,117 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
|
|
810
868
|
handle = raw_payload.get("handle") # from outer
|
811
869
|
adapter = raw_payload.get("adapter") # from outer
|
812
870
|
api_key = raw_payload.get("api_key") # from outer
|
871
|
+
|
872
|
+
print(f"[DEBUG] Extracted outer envelope data:")
|
873
|
+
print(f"[DEBUG] return_stream: {return_stream}")
|
874
|
+
print(f"[DEBUG] user_id: {user_id}")
|
875
|
+
print(f"[DEBUG] orgs: {orgs}")
|
876
|
+
print(f"[DEBUG] handle: {handle}")
|
877
|
+
print(f"[DEBUG] adapter: {adapter}")
|
878
|
+
print(f"[DEBUG] api_key length: {len(api_key) if api_key else 0}")
|
879
|
+
|
813
880
|
logger.debug(f">> Extracted API key length: {len(api_key) if api_key else 0}")
|
814
881
|
|
815
882
|
# --- Health Check Logic ---
|
816
883
|
# Use inner_kind for health check
|
817
|
-
if
|
884
|
+
print(f"[DEBUG] Checking if message is HealthCheck. inner_kind: '{inner_kind}'")
|
885
|
+
print(
|
886
|
+
f"[DEBUG] Message kind comparison: '{inner_kind}' == 'HealthCheckRequest' -> {inner_kind == 'HealthCheckRequest'}"
|
887
|
+
)
|
888
|
+
|
889
|
+
if inner_kind == "HealthCheckRequest":
|
890
|
+
print(f"[DEBUG] *** HEALTH CHECK REQUEST MESSAGE DETECTED ***")
|
891
|
+
print(f"[DEBUG] Message ID: {message_id}")
|
892
|
+
print(f"[DEBUG] Inner message ID: {inner_msg_id}")
|
893
|
+
print(f"[DEBUG] Return stream: {return_stream}")
|
894
|
+
print(f"[DEBUG] User ID: {user_id}")
|
895
|
+
print(f"[DEBUG] Content: {actual_content_to_process}")
|
896
|
+
|
818
897
|
logger.info(
|
819
|
-
f"Received
|
898
|
+
f"Received HealthCheckRequest message {message_id} (inner_id: {inner_msg_id})"
|
820
899
|
)
|
821
|
-
health_response = {
|
822
|
-
"kind": "StreamResponseMessage",
|
823
|
-
"id": message_id, # Use outer message_id for response ID
|
824
|
-
"content": {"status": "healthy", "checked_message_id": inner_msg_id},
|
825
|
-
"status": "success",
|
826
|
-
"created_at": datetime.now(timezone.utc).isoformat(),
|
827
|
-
"user_id": user_id,
|
828
|
-
}
|
829
|
-
if return_stream:
|
830
|
-
assert isinstance(return_stream, str)
|
831
|
-
r.xadd(return_stream, {"data": json.dumps(health_response)})
|
832
|
-
logger.info(f"Sent health check response to {return_stream}")
|
833
900
|
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
901
|
+
# Forward to health stream for health worker subprocess to process
|
902
|
+
if REDIS_HEALTH_STREAM:
|
903
|
+
print(
|
904
|
+
f"[DEBUG] Forwarding health check to health stream: {REDIS_HEALTH_STREAM}"
|
905
|
+
)
|
906
|
+
try:
|
907
|
+
# Forward the entire message data to the health stream
|
908
|
+
health_message_data = {
|
909
|
+
"data": json.dumps(
|
910
|
+
{
|
911
|
+
"kind": inner_kind,
|
912
|
+
"id": inner_msg_id,
|
913
|
+
"content": actual_content_to_process,
|
914
|
+
"created_at": inner_created_at.isoformat(),
|
915
|
+
"return_stream": return_stream,
|
916
|
+
"user_id": user_id,
|
917
|
+
"orgs": orgs,
|
918
|
+
"handle": handle,
|
919
|
+
"adapter": adapter,
|
920
|
+
"api_key": api_key,
|
921
|
+
"original_message_id": message_id, # Include original message ID for tracking
|
922
|
+
}
|
923
|
+
)
|
924
|
+
}
|
925
|
+
|
926
|
+
print(
|
927
|
+
f"[DEBUG] Health message data to forward: {json.dumps(health_message_data, indent=2)}"
|
928
|
+
)
|
929
|
+
|
930
|
+
r.xadd(REDIS_HEALTH_STREAM, health_message_data) # type: ignore[arg-type]
|
931
|
+
print(
|
932
|
+
f"[DEBUG] Health check forwarded successfully to {REDIS_HEALTH_STREAM}"
|
933
|
+
)
|
934
|
+
logger.info(
|
935
|
+
f"Forwarded HealthCheckRequest {message_id} to health stream {REDIS_HEALTH_STREAM}"
|
936
|
+
)
|
937
|
+
|
938
|
+
except Exception as e:
|
939
|
+
print(
|
940
|
+
f"[DEBUG] ERROR forwarding health check to health stream: {e}"
|
941
|
+
)
|
942
|
+
logger.error(f"Error forwarding health check to health stream: {e}")
|
943
|
+
# Fall back to sending error response directly
|
944
|
+
_send_error_response(
|
945
|
+
message_id,
|
946
|
+
f"Failed to forward health check: {e}",
|
947
|
+
traceback.format_exc(),
|
948
|
+
return_stream,
|
949
|
+
user_id,
|
950
|
+
)
|
951
|
+
else:
|
952
|
+
print(
|
953
|
+
f"[DEBUG] No health stream configured, cannot forward health check"
|
954
|
+
)
|
955
|
+
logger.warning(
|
956
|
+
"No health stream configured for health check forwarding"
|
957
|
+
)
|
958
|
+
# Send error response since we can't process health checks
|
959
|
+
_send_error_response(
|
960
|
+
message_id,
|
961
|
+
"Health check stream not configured",
|
962
|
+
"REDIS_HEALTH_STREAM environment variable not set",
|
963
|
+
return_stream,
|
964
|
+
user_id,
|
965
|
+
)
|
966
|
+
|
967
|
+
# Acknowledge the original message since we've forwarded it
|
968
|
+
print(f"[DEBUG] Acknowledging original health check message {message_id}")
|
969
|
+
try:
|
970
|
+
assert isinstance(REDIS_STREAM, str)
|
971
|
+
assert isinstance(REDIS_CONSUMER_GROUP, str)
|
972
|
+
r.xack(REDIS_STREAM, REDIS_CONSUMER_GROUP, message_id)
|
973
|
+
print(
|
974
|
+
f"[DEBUG] Health check message {message_id} acknowledged successfully"
|
975
|
+
)
|
976
|
+
logger.info(f"Acknowledged HealthCheckRequest message {message_id}")
|
977
|
+
except Exception as e:
|
978
|
+
print(f"[DEBUG] ERROR acknowledging health check message: {e}")
|
979
|
+
logger.error(f"Error acknowledging health check message: {e}")
|
980
|
+
|
981
|
+
print(f"[DEBUG] *** HEALTH CHECK FORWARDING COMPLETE ***")
|
838
982
|
return # Exit early for health checks
|
839
983
|
# --- End Health Check Logic ---
|
840
984
|
|
@@ -1210,19 +1354,31 @@ logger.info(
|
|
1210
1354
|
|
1211
1355
|
# Start the health check consumer subprocess
|
1212
1356
|
if REDIS_HEALTH_STREAM and REDIS_HEALTH_CONSUMER_GROUP:
|
1357
|
+
print(f"[DEBUG] === HEALTH SUBPROCESS INITIALIZATION ===")
|
1358
|
+
print(f"[DEBUG] REDIS_HEALTH_STREAM is set: {REDIS_HEALTH_STREAM}")
|
1359
|
+
print(f"[DEBUG] REDIS_HEALTH_CONSUMER_GROUP is set: {REDIS_HEALTH_CONSUMER_GROUP}")
|
1360
|
+
|
1213
1361
|
health_subprocess = start_health_check_subprocess()
|
1214
1362
|
if health_subprocess:
|
1363
|
+
print(
|
1364
|
+
f"[DEBUG] Health subprocess started successfully, starting monitor thread..."
|
1365
|
+
)
|
1215
1366
|
# Start monitoring thread for subprocess output
|
1216
1367
|
monitor_thread = threading.Thread(
|
1217
1368
|
target=monitor_health_subprocess, args=(health_subprocess,), daemon=True
|
1218
1369
|
)
|
1219
1370
|
monitor_thread.start()
|
1371
|
+
print(f"[DEBUG] Monitor thread started for health subprocess")
|
1220
1372
|
logger.info(
|
1221
1373
|
f"[Consumer] Health check subprocess for {REDIS_HEALTH_STREAM} started and monitoring thread started."
|
1222
1374
|
)
|
1223
1375
|
else:
|
1376
|
+
print(f"[DEBUG] Health subprocess failed to start")
|
1224
1377
|
logger.error("[Consumer] Failed to start health check subprocess.")
|
1225
1378
|
else:
|
1379
|
+
print(f"[DEBUG] === HEALTH SUBPROCESS NOT CONFIGURED ===")
|
1380
|
+
print(f"[DEBUG] REDIS_HEALTH_STREAM: {REDIS_HEALTH_STREAM}")
|
1381
|
+
print(f"[DEBUG] REDIS_HEALTH_CONSUMER_GROUP: {REDIS_HEALTH_CONSUMER_GROUP}")
|
1226
1382
|
logger.warning(
|
1227
1383
|
"[Consumer] Health check stream not configured. Health consumer subprocess not started."
|
1228
1384
|
)
|
@@ -1232,10 +1388,15 @@ try:
|
|
1232
1388
|
logger.debug(
|
1233
1389
|
f"[{datetime.now(timezone.utc).isoformat()}] --- Top of main loop ---"
|
1234
1390
|
) # Added log
|
1391
|
+
print(f"[DEBUG] === MAIN LOOP ITERATION START ===")
|
1235
1392
|
|
1236
1393
|
# --- Check Health Subprocess Status ---
|
1237
1394
|
if health_subprocess:
|
1238
|
-
|
1395
|
+
print(f"[DEBUG] Checking health subprocess status...")
|
1396
|
+
health_status = check_health_subprocess()
|
1397
|
+
print(f"[DEBUG] Health subprocess status check result: {health_status}")
|
1398
|
+
else:
|
1399
|
+
print(f"[DEBUG] No health subprocess to check")
|
1239
1400
|
|
1240
1401
|
# --- Check for Code Updates ---
|
1241
1402
|
if not disable_hot_reload:
|
@@ -1448,6 +1609,11 @@ try:
|
|
1448
1609
|
logger.debug(
|
1449
1610
|
f"[{datetime.now(timezone.utc).isoformat()}] Calling xreadgroup (block=5000ms)..."
|
1450
1611
|
) # Added log
|
1612
|
+
print(f"[DEBUG] About to call xreadgroup...")
|
1613
|
+
print(f"[DEBUG] Stream: {REDIS_STREAM}")
|
1614
|
+
print(f"[DEBUG] Consumer group: {REDIS_CONSUMER_GROUP}")
|
1615
|
+
print(f"[DEBUG] Consumer name: {consumer_name}")
|
1616
|
+
|
1451
1617
|
messages = r.xreadgroup(
|
1452
1618
|
REDIS_CONSUMER_GROUP,
|
1453
1619
|
consumer_name,
|
@@ -1456,10 +1622,14 @@ try:
|
|
1456
1622
|
block=5000, # Use milliseconds for block
|
1457
1623
|
)
|
1458
1624
|
|
1625
|
+
print(f"[DEBUG] xreadgroup returned: {messages}")
|
1626
|
+
print(f"[DEBUG] Messages type: {type(messages)}")
|
1627
|
+
|
1459
1628
|
if not messages:
|
1460
1629
|
logger.trace(
|
1461
1630
|
f"[{datetime.now(timezone.utc).isoformat()}] xreadgroup timed out (no new messages)."
|
1462
1631
|
) # Added log
|
1632
|
+
print(f"[DEBUG] No messages received (timeout or empty)")
|
1463
1633
|
# logger.debug("[Consumer] No new messages.") # Reduce verbosity
|
1464
1634
|
continue
|
1465
1635
|
# Removed the else block here
|
@@ -1475,6 +1645,8 @@ try:
|
|
1475
1645
|
stream_name_str, stream_messages = typed_messages[0]
|
1476
1646
|
num_msgs = len(stream_messages)
|
1477
1647
|
|
1648
|
+
print(f"[DEBUG] Processing {num_msgs} message(s) from stream {stream_name_str}")
|
1649
|
+
|
1478
1650
|
# Log reception and count before processing
|
1479
1651
|
logger.info(
|
1480
1652
|
f"[{datetime.now(timezone.utc).isoformat()}] xreadgroup returned {num_msgs} message(s). Processing..."
|
@@ -1492,8 +1664,13 @@ try:
|
|
1492
1664
|
# for k, v in msg_data_bytes_dict.items() } # No longer needed
|
1493
1665
|
# print(f"Processing message {message_id_str}") # Reduce verbosity
|
1494
1666
|
# print(f"Message data: {message_data_str_dict}") # Reduce verbosity
|
1667
|
+
print(f"[DEBUG] === PROCESSING MESSAGE {message_id_str} ===")
|
1668
|
+
print(f"[DEBUG] Message data keys: {list(message_data_str_dict.keys())}")
|
1669
|
+
|
1495
1670
|
process_message(message_id_str, message_data_str_dict)
|
1496
1671
|
|
1672
|
+
print(f"[DEBUG] === FINISHED PROCESSING MESSAGE {message_id_str} ===")
|
1673
|
+
|
1497
1674
|
except ConnectionError as e:
|
1498
1675
|
logger.error(f"Redis connection error: {e}. Reconnecting in 5s...")
|
1499
1676
|
time.sleep(5)
|
@@ -5,13 +5,16 @@ import os
|
|
5
5
|
import socket
|
6
6
|
import sys
|
7
7
|
import time
|
8
|
-
from typing import Dict, List, Optional, Tuple, cast
|
8
|
+
from typing import Any, Dict, List, Optional, Tuple, cast
|
9
9
|
|
10
10
|
import redis
|
11
11
|
import socks
|
12
12
|
from redis import ConnectionError, ResponseError
|
13
13
|
from redis.exceptions import TimeoutError as RedisTimeoutError
|
14
14
|
|
15
|
+
# Assuming these are imported from other modules
|
16
|
+
from nebu.processors.models import V1ProcessorHealthResponse
|
17
|
+
|
15
18
|
|
16
19
|
def setup_health_logging():
|
17
20
|
"""Set up logging for the health check worker to write to a dedicated file."""
|
@@ -48,31 +51,131 @@ def process_health_check_message(
|
|
48
51
|
health_group: str,
|
49
52
|
) -> None:
|
50
53
|
"""Processes a single health check message."""
|
54
|
+
print(f"[HEALTH DEBUG] === PROCESSING HEALTH CHECK MESSAGE ===")
|
55
|
+
print(f"[HEALTH DEBUG] Message ID: {message_id}")
|
56
|
+
print(f"[HEALTH DEBUG] Message data: {message_data}")
|
57
|
+
print(f"[HEALTH DEBUG] Health stream: {health_stream}")
|
58
|
+
print(f"[HEALTH DEBUG] Health group: {health_group}")
|
59
|
+
|
51
60
|
logger.info(f"Processing health check message {message_id}: {message_data}")
|
52
61
|
|
53
|
-
|
62
|
+
health_status = "ok"
|
63
|
+
health_message: Optional[str] = "Health check processed successfully."
|
64
|
+
details: Optional[Dict[str, Any]] = None
|
65
|
+
return_stream: Optional[str] = None
|
66
|
+
original_message_id: Optional[str] = None
|
67
|
+
user_id: Optional[str] = None
|
68
|
+
|
54
69
|
try:
|
55
70
|
if "data" in message_data:
|
56
|
-
data
|
71
|
+
print(f"[HEALTH DEBUG] Found 'data' field in message")
|
72
|
+
data_str = message_data["data"]
|
73
|
+
print(f"[HEALTH DEBUG] Raw data string: {data_str}")
|
74
|
+
|
75
|
+
data = json.loads(data_str)
|
76
|
+
print(f"[HEALTH DEBUG] Parsed data: {json.dumps(data, indent=2)}")
|
57
77
|
logger.info(f"Health check data: {data}")
|
78
|
+
|
79
|
+
# Extract important fields from the forwarded message
|
80
|
+
return_stream = data.get("return_stream")
|
81
|
+
original_message_id = data.get("original_message_id")
|
82
|
+
user_id = data.get("user_id")
|
83
|
+
inner_kind = data.get("kind")
|
84
|
+
inner_id = data.get("id")
|
85
|
+
content = data.get("content")
|
86
|
+
|
87
|
+
print(f"[HEALTH DEBUG] Extracted fields:")
|
88
|
+
print(f"[HEALTH DEBUG] return_stream: {return_stream}")
|
89
|
+
print(f"[HEALTH DEBUG] original_message_id: {original_message_id}")
|
90
|
+
print(f"[HEALTH DEBUG] user_id: {user_id}")
|
91
|
+
print(f"[HEALTH DEBUG] inner_kind: {inner_kind}")
|
92
|
+
print(f"[HEALTH DEBUG] inner_id: {inner_id}")
|
93
|
+
print(f"[HEALTH DEBUG] content: {content}")
|
94
|
+
|
95
|
+
# Update details with health check info
|
96
|
+
details = {
|
97
|
+
"checked_message_id": inner_id,
|
98
|
+
"original_message_id": original_message_id,
|
99
|
+
"health_stream": health_stream,
|
100
|
+
}
|
101
|
+
|
102
|
+
if content and isinstance(content, dict):
|
103
|
+
details.update({"check_content": content})
|
104
|
+
print(f"[HEALTH DEBUG] Added check_content to details")
|
105
|
+
else:
|
106
|
+
print(f"[HEALTH DEBUG] No 'data' field found in message_data")
|
107
|
+
|
58
108
|
except (json.JSONDecodeError, KeyError) as e:
|
109
|
+
print(f"[HEALTH DEBUG] ERROR parsing message data: {e}")
|
59
110
|
logger.warning(f"Could not parse health check message data: {e}")
|
111
|
+
health_status = "error"
|
112
|
+
health_message = f"Failed to parse health check message data: {e}"
|
113
|
+
details = {"error": str(e)}
|
114
|
+
|
115
|
+
# Construct the health response using V1ProcessorHealthResponse
|
116
|
+
health_response = V1ProcessorHealthResponse(
|
117
|
+
status=health_status, message=health_message, details=details
|
118
|
+
)
|
60
119
|
|
61
|
-
|
62
|
-
|
120
|
+
print(
|
121
|
+
f"[HEALTH DEBUG] Constructed V1ProcessorHealthResponse: {health_response.model_dump_json()}"
|
122
|
+
)
|
123
|
+
logger.info(
|
124
|
+
f"Health response for message {message_id}: {health_response.model_dump_json()}"
|
125
|
+
)
|
126
|
+
|
127
|
+
# Send the response to the return stream in the format expected by the system
|
128
|
+
if return_stream:
|
129
|
+
print(
|
130
|
+
f"[HEALTH DEBUG] Sending V1ProcessorHealthResponse to return_stream: {return_stream}"
|
131
|
+
)
|
132
|
+
try:
|
133
|
+
# Send the V1ProcessorHealthResponse directly (not wrapped in StreamResponseMessage)
|
134
|
+
response_data = health_response.model_dump()
|
135
|
+
|
136
|
+
print(
|
137
|
+
f"[HEALTH DEBUG] Sending V1ProcessorHealthResponse directly: {json.dumps(response_data, indent=2)}"
|
138
|
+
)
|
139
|
+
|
140
|
+
# Send to return stream
|
141
|
+
redis_conn.xadd(
|
142
|
+
return_stream,
|
143
|
+
{"data": json.dumps(response_data)},
|
144
|
+
maxlen=1000,
|
145
|
+
approximate=True,
|
146
|
+
)
|
147
|
+
print(
|
148
|
+
f"[HEALTH DEBUG] V1ProcessorHealthResponse sent successfully to {return_stream}"
|
149
|
+
)
|
150
|
+
logger.info(
|
151
|
+
f"Sent health response for {message_id} to stream: {return_stream}"
|
152
|
+
)
|
153
|
+
except Exception as e_resp_send:
|
154
|
+
print(f"[HEALTH DEBUG] ERROR sending response: {e_resp_send}")
|
155
|
+
logger.error(
|
156
|
+
f"Failed to send health response for {message_id} to stream {return_stream}: {e_resp_send}"
|
157
|
+
)
|
158
|
+
else:
|
159
|
+
print(f"[HEALTH DEBUG] No return_stream specified, not sending response")
|
63
160
|
|
64
161
|
# Acknowledge the health check message
|
162
|
+
print(f"[HEALTH DEBUG] Acknowledging message {message_id}")
|
65
163
|
try:
|
66
164
|
redis_conn.xack(health_stream, health_group, message_id)
|
165
|
+
print(f"[HEALTH DEBUG] Message {message_id} acknowledged successfully")
|
67
166
|
logger.info(f"Acknowledged health check message {message_id}")
|
68
167
|
except Exception as e_ack:
|
168
|
+
print(f"[HEALTH DEBUG] ERROR acknowledging message: {e_ack}")
|
69
169
|
logger.error(
|
70
170
|
f"Failed to acknowledge health check message {message_id}: {e_ack}"
|
71
171
|
)
|
72
172
|
|
173
|
+
print(f"[HEALTH DEBUG] === FINISHED PROCESSING HEALTH CHECK MESSAGE ===")
|
174
|
+
|
73
175
|
|
74
176
|
def main():
|
75
177
|
"""Main function for the health check consumer subprocess."""
|
178
|
+
print(f"[HEALTH DEBUG] === HEALTH WORKER STARTING ===")
|
76
179
|
logger = setup_health_logging()
|
77
180
|
|
78
181
|
# Get environment variables
|
@@ -80,7 +183,13 @@ def main():
|
|
80
183
|
health_stream = os.environ.get("REDIS_HEALTH_STREAM")
|
81
184
|
health_group = os.environ.get("REDIS_HEALTH_CONSUMER_GROUP")
|
82
185
|
|
186
|
+
print(f"[HEALTH DEBUG] Environment variables:")
|
187
|
+
print(f"[HEALTH DEBUG] REDIS_URL: {redis_url}")
|
188
|
+
print(f"[HEALTH DEBUG] REDIS_HEALTH_STREAM: {health_stream}")
|
189
|
+
print(f"[HEALTH DEBUG] REDIS_HEALTH_CONSUMER_GROUP: {health_group}")
|
190
|
+
|
83
191
|
if not all([redis_url, health_stream, health_group]):
|
192
|
+
print(f"[HEALTH DEBUG] ERROR: Missing required environment variables")
|
84
193
|
logger.error(
|
85
194
|
"Missing required environment variables: REDIS_URL, REDIS_HEALTH_STREAM, REDIS_HEALTH_CONSUMER_GROUP"
|
86
195
|
)
|
@@ -91,43 +200,64 @@ def main():
|
|
91
200
|
assert isinstance(health_stream, str)
|
92
201
|
assert isinstance(health_group, str)
|
93
202
|
|
203
|
+
print(
|
204
|
+
f"[HEALTH DEBUG] Starting health consumer for stream: {health_stream}, group: {health_group}"
|
205
|
+
)
|
94
206
|
logger.info(
|
95
207
|
f"Starting health consumer for stream: {health_stream}, group: {health_group}"
|
96
208
|
)
|
97
209
|
|
98
210
|
# Configure SOCKS proxy
|
211
|
+
print(f"[HEALTH DEBUG] Configuring SOCKS proxy...")
|
99
212
|
socks.set_default_proxy(socks.SOCKS5, "localhost", 1055)
|
100
213
|
socket.socket = socks.socksocket
|
101
214
|
logger.info("Configured SOCKS5 proxy for socket connections via localhost:1055")
|
102
215
|
|
103
216
|
health_redis_conn: Optional[redis.Redis] = None
|
104
217
|
health_consumer_name = f"health-consumer-{os.getpid()}-{socket.gethostname()}"
|
218
|
+
print(f"[HEALTH DEBUG] Health consumer name: {health_consumer_name}")
|
105
219
|
|
220
|
+
print(f"[HEALTH DEBUG] === ENTERING MAIN LOOP ===")
|
106
221
|
while True:
|
107
222
|
try:
|
108
223
|
if health_redis_conn is None:
|
224
|
+
print(f"[HEALTH DEBUG] Connecting to Redis for health stream...")
|
109
225
|
logger.info("Connecting to Redis for health stream...")
|
110
226
|
health_redis_conn = redis.from_url(redis_url, decode_responses=True)
|
111
227
|
health_redis_conn.ping()
|
228
|
+
print(f"[HEALTH DEBUG] Connected to Redis successfully")
|
112
229
|
logger.info("Connected to Redis for health stream.")
|
113
230
|
|
114
231
|
# Create health consumer group if it doesn't exist
|
232
|
+
print(f"[HEALTH DEBUG] Creating/checking consumer group...")
|
115
233
|
try:
|
116
234
|
health_redis_conn.xgroup_create(
|
117
235
|
health_stream, health_group, id="0", mkstream=True
|
118
236
|
)
|
237
|
+
print(
|
238
|
+
f"[HEALTH DEBUG] Created consumer group {health_group} for stream {health_stream}"
|
239
|
+
)
|
119
240
|
logger.info(
|
120
241
|
f"Created consumer group {health_group} for stream {health_stream}"
|
121
242
|
)
|
122
243
|
except ResponseError as e_group:
|
123
244
|
if "BUSYGROUP" in str(e_group):
|
245
|
+
print(
|
246
|
+
f"[HEALTH DEBUG] Consumer group {health_group} already exists"
|
247
|
+
)
|
124
248
|
logger.info(f"Consumer group {health_group} already exists.")
|
125
249
|
else:
|
250
|
+
print(
|
251
|
+
f"[HEALTH DEBUG] ERROR creating health consumer group: {e_group}"
|
252
|
+
)
|
126
253
|
logger.error(f"Error creating health consumer group: {e_group}")
|
127
254
|
time.sleep(5)
|
128
255
|
health_redis_conn = None
|
129
256
|
continue
|
130
257
|
except Exception as e_group_other:
|
258
|
+
print(
|
259
|
+
f"[HEALTH DEBUG] UNEXPECTED ERROR creating health consumer group: {e_group_other}"
|
260
|
+
)
|
131
261
|
logger.error(
|
132
262
|
f"Unexpected error creating health consumer group: {e_group_other}"
|
133
263
|
)
|
@@ -138,6 +268,7 @@ def main():
|
|
138
268
|
# Read from health stream
|
139
269
|
assert health_redis_conn is not None
|
140
270
|
|
271
|
+
print(f"[HEALTH DEBUG] Reading from health stream {health_stream}...")
|
141
272
|
health_streams_arg: Dict[str, object] = {health_stream: ">"}
|
142
273
|
raw_messages = health_redis_conn.xreadgroup(
|
143
274
|
health_group,
|
@@ -147,13 +278,21 @@ def main():
|
|
147
278
|
block=5000, # Block for 5 seconds
|
148
279
|
)
|
149
280
|
|
281
|
+
print(f"[HEALTH DEBUG] xreadgroup returned: {raw_messages}")
|
282
|
+
print(f"[HEALTH DEBUG] Messages type: {type(raw_messages)}")
|
283
|
+
|
150
284
|
if raw_messages:
|
285
|
+
print(f"[HEALTH DEBUG] Found messages to process")
|
151
286
|
# Cast to expected type for decode_responses=True
|
152
287
|
messages = cast(
|
153
288
|
List[Tuple[str, List[Tuple[str, Dict[str, str]]]]], raw_messages
|
154
289
|
)
|
155
|
-
for
|
290
|
+
for stream_name, stream_messages in messages:
|
291
|
+
print(
|
292
|
+
f"[HEALTH DEBUG] Processing stream: {stream_name} with {len(stream_messages)} message(s)"
|
293
|
+
)
|
156
294
|
for message_id, message_data in stream_messages:
|
295
|
+
print(f"[HEALTH DEBUG] Processing message {message_id}")
|
157
296
|
process_health_check_message(
|
158
297
|
message_id,
|
159
298
|
message_data,
|
@@ -162,6 +301,8 @@ def main():
|
|
162
301
|
health_stream,
|
163
302
|
health_group,
|
164
303
|
)
|
304
|
+
else:
|
305
|
+
print(f"[HEALTH DEBUG] No messages received (timeout)")
|
165
306
|
|
166
307
|
except (ConnectionError, RedisTimeoutError, TimeoutError) as e_conn:
|
167
308
|
logger.error(f"Redis connection error: {e_conn}. Reconnecting in 5s...")
|
@@ -149,3 +149,9 @@ class V1OpenAIStreamResponse(BaseModel):
|
|
149
149
|
content: Any # Using Any for ChatCompletionResponse
|
150
150
|
created_at: int
|
151
151
|
user_id: Optional[str] = None
|
152
|
+
|
153
|
+
|
154
|
+
class V1ProcessorHealthResponse(BaseModel):
|
155
|
+
status: str
|
156
|
+
message: Optional[str] = None
|
157
|
+
details: Optional[Any] = None
|
@@ -1,7 +1,8 @@
|
|
1
1
|
import json
|
2
2
|
import threading
|
3
3
|
import time
|
4
|
-
|
4
|
+
|
5
|
+
# import uuid # Removed unused import
|
5
6
|
from typing import (
|
6
7
|
Any,
|
7
8
|
Dict,
|
@@ -23,6 +24,7 @@ from nebu.meta import V1ResourceMetaRequest, V1ResourceReference
|
|
23
24
|
from nebu.processors.models import (
|
24
25
|
V1ContainerRequest,
|
25
26
|
V1Processor,
|
27
|
+
V1ProcessorHealthResponse,
|
26
28
|
V1ProcessorRequest,
|
27
29
|
V1Processors,
|
28
30
|
V1ProcessorScaleRequest,
|
@@ -246,7 +248,7 @@ class Processor(Generic[InputType, OutputType]):
|
|
246
248
|
|
247
249
|
# --- Wait for health check if requested ---
|
248
250
|
if wait_for_healthy:
|
249
|
-
self.
|
251
|
+
self.wait_for_healthy()
|
250
252
|
|
251
253
|
def __call__(
|
252
254
|
self,
|
@@ -559,11 +561,11 @@ class Processor(Generic[InputType, OutputType]):
|
|
559
561
|
else:
|
560
562
|
logger.info(f"No active log stream to stop for {self.name}.")
|
561
563
|
|
562
|
-
def
|
564
|
+
def wait_for_healthy(
|
563
565
|
self, timeout: float = 3600.0, retry_interval: float = 5.0
|
564
566
|
) -> None:
|
565
567
|
"""
|
566
|
-
Wait for the processor to respond to health checks.
|
568
|
+
Wait for the processor to respond to health checks using the health endpoint.
|
567
569
|
|
568
570
|
Args:
|
569
571
|
timeout: Maximum time to wait for health check in seconds
|
@@ -573,40 +575,25 @@ class Processor(Generic[InputType, OutputType]):
|
|
573
575
|
raise ValueError("Processor not found, cannot perform health check")
|
574
576
|
|
575
577
|
logger.info(
|
576
|
-
f"Waiting for processor {self.processor.metadata.name} to be healthy..."
|
578
|
+
f"Waiting for processor {self.processor.metadata.name} to be healthy via health endpoint..."
|
577
579
|
)
|
578
580
|
|
579
581
|
start_time = time.time()
|
580
582
|
while time.time() - start_time < timeout:
|
581
583
|
try:
|
582
|
-
#
|
583
|
-
|
584
|
-
"
|
585
|
-
"id": str(uuid.uuid4()),
|
586
|
-
"content": {},
|
587
|
-
"created_at": time.time(),
|
588
|
-
}
|
589
|
-
|
590
|
-
# Send health check and wait for response
|
591
|
-
response = self.send(
|
592
|
-
data=health_check_data, # type: ignore[arg-type]
|
593
|
-
wait=True,
|
594
|
-
timeout=30.0, # Short timeout for individual health check
|
584
|
+
health_response = self.health() # Use the new health() method
|
585
|
+
logger.info(
|
586
|
+
f">>> Health check response: {health_response.model_dump_json()}"
|
595
587
|
)
|
596
|
-
logger.info(f">>> Health check response: {response}")
|
597
588
|
|
598
589
|
# Check if the response indicates health
|
599
|
-
if
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
logger.info(
|
608
|
-
f"Health check attempt failed, retrying in {retry_interval}s..."
|
609
|
-
)
|
590
|
+
if health_response.status == "ok": # Check for "ok" status
|
591
|
+
logger.info(f"Processor {self.processor.metadata.name} is healthy!")
|
592
|
+
return
|
593
|
+
else:
|
594
|
+
logger.info(
|
595
|
+
f"Processor {self.processor.metadata.name} reported status: {health_response.status}. Retrying in {retry_interval}s..."
|
596
|
+
)
|
610
597
|
|
611
598
|
except Exception as e:
|
612
599
|
logger.info(
|
@@ -619,3 +606,39 @@ class Processor(Generic[InputType, OutputType]):
|
|
619
606
|
raise TimeoutError(
|
620
607
|
f"Processor {self.processor.metadata.name} failed to become healthy within {timeout} seconds"
|
621
608
|
)
|
609
|
+
|
610
|
+
def health(self) -> V1ProcessorHealthResponse:
|
611
|
+
"""
|
612
|
+
Performs a health check on the processor by calling the health endpoint.
|
613
|
+
"""
|
614
|
+
if (
|
615
|
+
not self.processor
|
616
|
+
or not self.processor.metadata.name
|
617
|
+
or not self.processor.metadata.namespace
|
618
|
+
):
|
619
|
+
raise ValueError(
|
620
|
+
"Processor not found or missing metadata (name/namespace), cannot perform health check."
|
621
|
+
)
|
622
|
+
|
623
|
+
health_url = f"{self.orign_host}/v1/processors/{self.processor.metadata.namespace}/{self.processor.metadata.name}/health"
|
624
|
+
logger.debug(f"Calling health check endpoint: {health_url}")
|
625
|
+
|
626
|
+
try:
|
627
|
+
response = requests.get(
|
628
|
+
health_url,
|
629
|
+
headers={"Authorization": f"Bearer {self.api_key}"},
|
630
|
+
timeout=30.0, # Standard timeout for a health check
|
631
|
+
)
|
632
|
+
response.raise_for_status() # Raise an exception for HTTP errors
|
633
|
+
health_response_data = response.json()
|
634
|
+
return V1ProcessorHealthResponse.model_validate(health_response_data)
|
635
|
+
except requests.exceptions.RequestException as e:
|
636
|
+
logger.error(f"Health check request to {health_url} failed: {e}")
|
637
|
+
# Optionally, return a V1ProcessorHealthResponse indicating an error
|
638
|
+
# For now, re-raising the exception or a custom one might be better
|
639
|
+
raise RuntimeError(f"Failed to get health status: {e}") from e
|
640
|
+
except Exception as e:
|
641
|
+
logger.error(f"An unexpected error occurred during health check: {e}")
|
642
|
+
raise RuntimeError(
|
643
|
+
f"Unexpected error during health status retrieval: {e}"
|
644
|
+
) from e
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|