nebu 0.1.118__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.118/src/nebu.egg-info → nebu-0.1.119}/PKG-INFO +1 -1
- {nebu-0.1.118 → nebu-0.1.119}/pyproject.toml +1 -1
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/processors/consumer.py +197 -20
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/processors/consumer_health_worker.py +111 -10
- {nebu-0.1.118 → nebu-0.1.119/src/nebu.egg-info}/PKG-INFO +1 -1
- {nebu-0.1.118 → nebu-0.1.119}/LICENSE +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/README.md +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/setup.cfg +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/__init__.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/auth.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/builders/builder.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/builders/models.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/cache.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/config.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/containers/container.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/containers/models.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/data.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/errors.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/logging.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/meta.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/namespaces/models.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/namespaces/namespace.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/orign.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/processors/consumer_process_worker.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/processors/decorate.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/processors/default.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/processors/models.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/processors/processor.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/redis/models.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu/services/service.py +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu.egg-info/SOURCES.txt +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu.egg-info/dependency_links.txt +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu.egg-info/requires.txt +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/src/nebu.egg-info/top_level.txt +0 -0
- {nebu-0.1.118 → nebu-0.1.119}/tests/test_bucket.py +0 -0
- {nebu-0.1.118 → 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)
|
@@ -51,68 +51,131 @@ def process_health_check_message(
|
|
51
51
|
health_group: str,
|
52
52
|
) -> None:
|
53
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
|
+
|
54
60
|
logger.info(f"Processing health check message {message_id}: {message_data}")
|
55
61
|
|
56
62
|
health_status = "ok"
|
57
63
|
health_message: Optional[str] = "Health check processed successfully."
|
58
64
|
details: Optional[Dict[str, Any]] = None
|
59
65
|
return_stream: Optional[str] = None
|
66
|
+
original_message_id: Optional[str] = None
|
67
|
+
user_id: Optional[str] = None
|
60
68
|
|
61
69
|
try:
|
62
70
|
if "data" in message_data:
|
63
|
-
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)}")
|
64
77
|
logger.info(f"Health check data: {data}")
|
65
|
-
|
78
|
+
|
79
|
+
# Extract important fields from the forwarded message
|
66
80
|
return_stream = data.get("return_stream")
|
67
|
-
|
68
|
-
|
69
|
-
|
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")
|
70
107
|
|
71
108
|
except (json.JSONDecodeError, KeyError) as e:
|
109
|
+
print(f"[HEALTH DEBUG] ERROR parsing message data: {e}")
|
72
110
|
logger.warning(f"Could not parse health check message data: {e}")
|
73
111
|
health_status = "error"
|
74
112
|
health_message = f"Failed to parse health check message data: {e}"
|
75
113
|
details = {"error": str(e)}
|
76
114
|
|
77
|
-
# Construct the health response
|
115
|
+
# Construct the health response using V1ProcessorHealthResponse
|
78
116
|
health_response = V1ProcessorHealthResponse(
|
79
117
|
status=health_status, message=health_message, details=details
|
80
118
|
)
|
81
119
|
|
120
|
+
print(
|
121
|
+
f"[HEALTH DEBUG] Constructed V1ProcessorHealthResponse: {health_response.model_dump_json()}"
|
122
|
+
)
|
82
123
|
logger.info(
|
83
124
|
f"Health response for message {message_id}: {health_response.model_dump_json()}"
|
84
125
|
)
|
85
126
|
|
86
|
-
#
|
127
|
+
# Send the response to the return stream in the format expected by the system
|
87
128
|
if return_stream:
|
129
|
+
print(
|
130
|
+
f"[HEALTH DEBUG] Sending V1ProcessorHealthResponse to return_stream: {return_stream}"
|
131
|
+
)
|
88
132
|
try:
|
89
|
-
#
|
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
|
90
141
|
redis_conn.xadd(
|
91
142
|
return_stream,
|
92
|
-
|
143
|
+
{"data": json.dumps(response_data)},
|
93
144
|
maxlen=1000,
|
94
145
|
approximate=True,
|
95
146
|
)
|
147
|
+
print(
|
148
|
+
f"[HEALTH DEBUG] V1ProcessorHealthResponse sent successfully to {return_stream}"
|
149
|
+
)
|
96
150
|
logger.info(
|
97
151
|
f"Sent health response for {message_id} to stream: {return_stream}"
|
98
152
|
)
|
99
153
|
except Exception as e_resp_send:
|
154
|
+
print(f"[HEALTH DEBUG] ERROR sending response: {e_resp_send}")
|
100
155
|
logger.error(
|
101
156
|
f"Failed to send health response for {message_id} to stream {return_stream}: {e_resp_send}"
|
102
157
|
)
|
158
|
+
else:
|
159
|
+
print(f"[HEALTH DEBUG] No return_stream specified, not sending response")
|
103
160
|
|
104
161
|
# Acknowledge the health check message
|
162
|
+
print(f"[HEALTH DEBUG] Acknowledging message {message_id}")
|
105
163
|
try:
|
106
164
|
redis_conn.xack(health_stream, health_group, message_id)
|
165
|
+
print(f"[HEALTH DEBUG] Message {message_id} acknowledged successfully")
|
107
166
|
logger.info(f"Acknowledged health check message {message_id}")
|
108
167
|
except Exception as e_ack:
|
168
|
+
print(f"[HEALTH DEBUG] ERROR acknowledging message: {e_ack}")
|
109
169
|
logger.error(
|
110
170
|
f"Failed to acknowledge health check message {message_id}: {e_ack}"
|
111
171
|
)
|
112
172
|
|
173
|
+
print(f"[HEALTH DEBUG] === FINISHED PROCESSING HEALTH CHECK MESSAGE ===")
|
174
|
+
|
113
175
|
|
114
176
|
def main():
|
115
177
|
"""Main function for the health check consumer subprocess."""
|
178
|
+
print(f"[HEALTH DEBUG] === HEALTH WORKER STARTING ===")
|
116
179
|
logger = setup_health_logging()
|
117
180
|
|
118
181
|
# Get environment variables
|
@@ -120,7 +183,13 @@ def main():
|
|
120
183
|
health_stream = os.environ.get("REDIS_HEALTH_STREAM")
|
121
184
|
health_group = os.environ.get("REDIS_HEALTH_CONSUMER_GROUP")
|
122
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
|
+
|
123
191
|
if not all([redis_url, health_stream, health_group]):
|
192
|
+
print(f"[HEALTH DEBUG] ERROR: Missing required environment variables")
|
124
193
|
logger.error(
|
125
194
|
"Missing required environment variables: REDIS_URL, REDIS_HEALTH_STREAM, REDIS_HEALTH_CONSUMER_GROUP"
|
126
195
|
)
|
@@ -131,43 +200,64 @@ def main():
|
|
131
200
|
assert isinstance(health_stream, str)
|
132
201
|
assert isinstance(health_group, str)
|
133
202
|
|
203
|
+
print(
|
204
|
+
f"[HEALTH DEBUG] Starting health consumer for stream: {health_stream}, group: {health_group}"
|
205
|
+
)
|
134
206
|
logger.info(
|
135
207
|
f"Starting health consumer for stream: {health_stream}, group: {health_group}"
|
136
208
|
)
|
137
209
|
|
138
210
|
# Configure SOCKS proxy
|
211
|
+
print(f"[HEALTH DEBUG] Configuring SOCKS proxy...")
|
139
212
|
socks.set_default_proxy(socks.SOCKS5, "localhost", 1055)
|
140
213
|
socket.socket = socks.socksocket
|
141
214
|
logger.info("Configured SOCKS5 proxy for socket connections via localhost:1055")
|
142
215
|
|
143
216
|
health_redis_conn: Optional[redis.Redis] = None
|
144
217
|
health_consumer_name = f"health-consumer-{os.getpid()}-{socket.gethostname()}"
|
218
|
+
print(f"[HEALTH DEBUG] Health consumer name: {health_consumer_name}")
|
145
219
|
|
220
|
+
print(f"[HEALTH DEBUG] === ENTERING MAIN LOOP ===")
|
146
221
|
while True:
|
147
222
|
try:
|
148
223
|
if health_redis_conn is None:
|
224
|
+
print(f"[HEALTH DEBUG] Connecting to Redis for health stream...")
|
149
225
|
logger.info("Connecting to Redis for health stream...")
|
150
226
|
health_redis_conn = redis.from_url(redis_url, decode_responses=True)
|
151
227
|
health_redis_conn.ping()
|
228
|
+
print(f"[HEALTH DEBUG] Connected to Redis successfully")
|
152
229
|
logger.info("Connected to Redis for health stream.")
|
153
230
|
|
154
231
|
# Create health consumer group if it doesn't exist
|
232
|
+
print(f"[HEALTH DEBUG] Creating/checking consumer group...")
|
155
233
|
try:
|
156
234
|
health_redis_conn.xgroup_create(
|
157
235
|
health_stream, health_group, id="0", mkstream=True
|
158
236
|
)
|
237
|
+
print(
|
238
|
+
f"[HEALTH DEBUG] Created consumer group {health_group} for stream {health_stream}"
|
239
|
+
)
|
159
240
|
logger.info(
|
160
241
|
f"Created consumer group {health_group} for stream {health_stream}"
|
161
242
|
)
|
162
243
|
except ResponseError as e_group:
|
163
244
|
if "BUSYGROUP" in str(e_group):
|
245
|
+
print(
|
246
|
+
f"[HEALTH DEBUG] Consumer group {health_group} already exists"
|
247
|
+
)
|
164
248
|
logger.info(f"Consumer group {health_group} already exists.")
|
165
249
|
else:
|
250
|
+
print(
|
251
|
+
f"[HEALTH DEBUG] ERROR creating health consumer group: {e_group}"
|
252
|
+
)
|
166
253
|
logger.error(f"Error creating health consumer group: {e_group}")
|
167
254
|
time.sleep(5)
|
168
255
|
health_redis_conn = None
|
169
256
|
continue
|
170
257
|
except Exception as e_group_other:
|
258
|
+
print(
|
259
|
+
f"[HEALTH DEBUG] UNEXPECTED ERROR creating health consumer group: {e_group_other}"
|
260
|
+
)
|
171
261
|
logger.error(
|
172
262
|
f"Unexpected error creating health consumer group: {e_group_other}"
|
173
263
|
)
|
@@ -178,6 +268,7 @@ def main():
|
|
178
268
|
# Read from health stream
|
179
269
|
assert health_redis_conn is not None
|
180
270
|
|
271
|
+
print(f"[HEALTH DEBUG] Reading from health stream {health_stream}...")
|
181
272
|
health_streams_arg: Dict[str, object] = {health_stream: ">"}
|
182
273
|
raw_messages = health_redis_conn.xreadgroup(
|
183
274
|
health_group,
|
@@ -187,13 +278,21 @@ def main():
|
|
187
278
|
block=5000, # Block for 5 seconds
|
188
279
|
)
|
189
280
|
|
281
|
+
print(f"[HEALTH DEBUG] xreadgroup returned: {raw_messages}")
|
282
|
+
print(f"[HEALTH DEBUG] Messages type: {type(raw_messages)}")
|
283
|
+
|
190
284
|
if raw_messages:
|
285
|
+
print(f"[HEALTH DEBUG] Found messages to process")
|
191
286
|
# Cast to expected type for decode_responses=True
|
192
287
|
messages = cast(
|
193
288
|
List[Tuple[str, List[Tuple[str, Dict[str, str]]]]], raw_messages
|
194
289
|
)
|
195
|
-
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
|
+
)
|
196
294
|
for message_id, message_data in stream_messages:
|
295
|
+
print(f"[HEALTH DEBUG] Processing message {message_id}")
|
197
296
|
process_health_check_message(
|
198
297
|
message_id,
|
199
298
|
message_data,
|
@@ -202,6 +301,8 @@ def main():
|
|
202
301
|
health_stream,
|
203
302
|
health_group,
|
204
303
|
)
|
304
|
+
else:
|
305
|
+
print(f"[HEALTH DEBUG] No messages received (timeout)")
|
205
306
|
|
206
307
|
except (ConnectionError, RedisTimeoutError, TimeoutError) as e_conn:
|
207
308
|
logger.error(f"Redis connection error: {e_conn}. Reconnecting in 5s...")
|
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
|
File without changes
|
File without changes
|