nebu 0.1.118__py3-none-any.whl → 0.1.120__py3-none-any.whl
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/processors/consumer.py +221 -20
- nebu/processors/consumer_health_worker.py +111 -10
- {nebu-0.1.118.dist-info → nebu-0.1.120.dist-info}/METADATA +1 -1
- {nebu-0.1.118.dist-info → nebu-0.1.120.dist-info}/RECORD +7 -7
- {nebu-0.1.118.dist-info → nebu-0.1.120.dist-info}/WHEEL +1 -1
- {nebu-0.1.118.dist-info → nebu-0.1.120.dist-info}/licenses/LICENSE +0 -0
- {nebu-0.1.118.dist-info → nebu-0.1.120.dist-info}/top_level.txt +0 -0
nebu/processors/consumer.py
CHANGED
@@ -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,69 @@ 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
|
|
480
|
+
# Cat the health subprocess log file
|
481
|
+
try:
|
482
|
+
log_dir = os.path.join(os.getcwd(), "logs")
|
483
|
+
log_file = os.path.join(log_dir, f"health_consumer_{health_subprocess.pid}.log")
|
484
|
+
|
485
|
+
if os.path.exists(log_file):
|
486
|
+
print(
|
487
|
+
f"[DEBUG] === HEALTH SUBPROCESS LOG (PID {health_subprocess.pid}) ==="
|
488
|
+
)
|
489
|
+
try:
|
490
|
+
with open(log_file, "r") as f:
|
491
|
+
log_contents = f.read()
|
492
|
+
if log_contents.strip():
|
493
|
+
print(log_contents)
|
494
|
+
else:
|
495
|
+
print("[DEBUG] Log file is empty")
|
496
|
+
except Exception as e:
|
497
|
+
print(f"[DEBUG] Error reading log file {log_file}: {e}")
|
498
|
+
print(f"[DEBUG] === END HEALTH SUBPROCESS LOG ===")
|
499
|
+
else:
|
500
|
+
print(f"[DEBUG] Health subprocess log file not found: {log_file}")
|
501
|
+
except Exception as e:
|
502
|
+
print(f"[DEBUG] Error accessing health subprocess log: {e}")
|
503
|
+
|
455
504
|
# Check if process is still running
|
456
|
-
|
505
|
+
poll_result = health_subprocess.poll()
|
506
|
+
print(f"[DEBUG] health_subprocess.poll() returned: {poll_result}")
|
507
|
+
|
508
|
+
if poll_result is None:
|
509
|
+
print(f"[DEBUG] Health subprocess still running (PID {health_subprocess.pid})")
|
457
510
|
return True # Still running
|
458
511
|
|
459
512
|
# Process has exited
|
460
513
|
exit_code = health_subprocess.returncode
|
514
|
+
print(f"[DEBUG] Health subprocess exited with code {exit_code}")
|
461
515
|
logger.warning(
|
462
516
|
f"[Consumer] Health subprocess exited with code {exit_code}. Restarting..."
|
463
517
|
)
|
464
518
|
|
465
519
|
# Start a new health subprocess
|
520
|
+
print(f"[DEBUG] Attempting to restart health subprocess...")
|
466
521
|
health_subprocess = start_health_check_subprocess()
|
467
522
|
|
468
523
|
if health_subprocess:
|
524
|
+
print(f"[DEBUG] Health subprocess restarted successfully")
|
469
525
|
# Start monitoring thread for the new subprocess
|
470
526
|
monitor_thread = threading.Thread(
|
471
527
|
target=monitor_health_subprocess, args=(health_subprocess,), daemon=True
|
472
528
|
)
|
473
529
|
monitor_thread.start()
|
530
|
+
print(f"[DEBUG] Monitor thread started for health subprocess")
|
474
531
|
logger.info(
|
475
532
|
"[Consumer] Health subprocess restarted and monitoring thread started."
|
476
533
|
)
|
477
534
|
return True
|
478
535
|
else:
|
536
|
+
print(f"[DEBUG] Failed to restart health subprocess")
|
479
537
|
logger.error("[Consumer] Failed to restart health subprocess.")
|
480
538
|
return False
|
481
539
|
|
@@ -714,6 +772,9 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
|
|
714
772
|
user_id = None
|
715
773
|
try:
|
716
774
|
payload_str = message_data.get("data")
|
775
|
+
print(f"[DEBUG] Raw message_data: {message_data}")
|
776
|
+
print(f"[DEBUG] Payload string: {payload_str}")
|
777
|
+
|
717
778
|
if (
|
718
779
|
not payload_str
|
719
780
|
): # Covers None and empty string, isinstance check is redundant
|
@@ -722,6 +783,7 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
|
|
722
783
|
)
|
723
784
|
try:
|
724
785
|
raw_payload = json.loads(payload_str)
|
786
|
+
print(f"[DEBUG] Parsed raw_payload: {json.dumps(raw_payload, indent=2)}")
|
725
787
|
except json.JSONDecodeError as json_err:
|
726
788
|
raise ValueError(f"Failed to parse JSON payload: {json_err}") from json_err
|
727
789
|
if not isinstance(raw_payload, dict):
|
@@ -734,6 +796,9 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
|
|
734
796
|
# --- Extract fields from the *inner* message content for HealthCheck and regular processing ---
|
735
797
|
# The actual message content is inside raw_payload["content"]
|
736
798
|
inner_content_data = raw_payload.get("content", {})
|
799
|
+
print(
|
800
|
+
f"[DEBUG] Extracted inner_content_data: {json.dumps(inner_content_data, indent=2) if isinstance(inner_content_data, dict) else inner_content_data}"
|
801
|
+
)
|
737
802
|
|
738
803
|
# Add debug logging for content structure analysis
|
739
804
|
logger.debug(f">> inner_content_data type: {type(inner_content_data)}")
|
@@ -746,6 +811,7 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
|
|
746
811
|
# we can't reliably get 'kind' or other fields from it.
|
747
812
|
# This case is more relevant for non-StreamMessage processors.
|
748
813
|
# For HealthChecks, we expect a structured 'content'.
|
814
|
+
print(f"[DEBUG] inner_content_data is not a dict, using defaults")
|
749
815
|
logger.warning(
|
750
816
|
f"Received non-dict inner_content_data: {inner_content_data}. HealthCheck might be missed if applicable."
|
751
817
|
)
|
@@ -762,6 +828,8 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
|
|
762
828
|
and "content" in inner_content_data
|
763
829
|
)
|
764
830
|
|
831
|
+
print(f"[DEBUG] has_message_structure: {has_message_structure}")
|
832
|
+
print(f"[DEBUG] inner_content_data keys: {list(inner_content_data.keys())}")
|
765
833
|
logger.debug(f">> has_message_structure: {has_message_structure}")
|
766
834
|
|
767
835
|
if has_message_structure:
|
@@ -770,6 +838,13 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
|
|
770
838
|
inner_msg_id = inner_content_data.get("id", "")
|
771
839
|
actual_content_to_process = inner_content_data.get("content", {})
|
772
840
|
inner_created_at_str = inner_content_data.get("created_at")
|
841
|
+
print(f"[DEBUG] Using nested structure:")
|
842
|
+
print(f"[DEBUG] inner_kind: '{inner_kind}'")
|
843
|
+
print(f"[DEBUG] inner_msg_id: '{inner_msg_id}'")
|
844
|
+
print(
|
845
|
+
f"[DEBUG] actual_content_to_process: {actual_content_to_process}"
|
846
|
+
)
|
847
|
+
print(f"[DEBUG] inner_created_at_str: {inner_created_at_str}")
|
773
848
|
logger.debug(
|
774
849
|
f">> Using nested structure - inner_kind: {inner_kind}, inner_msg_id: {inner_msg_id}"
|
775
850
|
)
|
@@ -786,6 +861,13 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
|
|
786
861
|
inner_created_at_str = raw_payload.get(
|
787
862
|
"created_at"
|
788
863
|
) # Get created_at from outer payload
|
864
|
+
print(f"[DEBUG] Using direct structure:")
|
865
|
+
print(f"[DEBUG] inner_kind: '{inner_kind}' (from outer payload)")
|
866
|
+
print(f"[DEBUG] inner_msg_id: '{inner_msg_id}' (from outer payload)")
|
867
|
+
print(
|
868
|
+
f"[DEBUG] actual_content_to_process: {actual_content_to_process}"
|
869
|
+
)
|
870
|
+
print(f"[DEBUG] inner_created_at_str: {inner_created_at_str}")
|
789
871
|
logger.debug(
|
790
872
|
f">> Using direct structure - inner_kind: {inner_kind}, inner_msg_id: {inner_msg_id}"
|
791
873
|
)
|
@@ -810,31 +892,117 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
|
|
810
892
|
handle = raw_payload.get("handle") # from outer
|
811
893
|
adapter = raw_payload.get("adapter") # from outer
|
812
894
|
api_key = raw_payload.get("api_key") # from outer
|
895
|
+
|
896
|
+
print(f"[DEBUG] Extracted outer envelope data:")
|
897
|
+
print(f"[DEBUG] return_stream: {return_stream}")
|
898
|
+
print(f"[DEBUG] user_id: {user_id}")
|
899
|
+
print(f"[DEBUG] orgs: {orgs}")
|
900
|
+
print(f"[DEBUG] handle: {handle}")
|
901
|
+
print(f"[DEBUG] adapter: {adapter}")
|
902
|
+
print(f"[DEBUG] api_key length: {len(api_key) if api_key else 0}")
|
903
|
+
|
813
904
|
logger.debug(f">> Extracted API key length: {len(api_key) if api_key else 0}")
|
814
905
|
|
815
906
|
# --- Health Check Logic ---
|
816
907
|
# Use inner_kind for health check
|
817
|
-
if
|
908
|
+
print(f"[DEBUG] Checking if message is HealthCheck. inner_kind: '{inner_kind}'")
|
909
|
+
print(
|
910
|
+
f"[DEBUG] Message kind comparison: '{inner_kind}' == 'HealthCheckRequest' -> {inner_kind == 'HealthCheckRequest'}"
|
911
|
+
)
|
912
|
+
|
913
|
+
if inner_kind == "HealthCheckRequest":
|
914
|
+
print(f"[DEBUG] *** HEALTH CHECK REQUEST MESSAGE DETECTED ***")
|
915
|
+
print(f"[DEBUG] Message ID: {message_id}")
|
916
|
+
print(f"[DEBUG] Inner message ID: {inner_msg_id}")
|
917
|
+
print(f"[DEBUG] Return stream: {return_stream}")
|
918
|
+
print(f"[DEBUG] User ID: {user_id}")
|
919
|
+
print(f"[DEBUG] Content: {actual_content_to_process}")
|
920
|
+
|
818
921
|
logger.info(
|
819
|
-
f"Received
|
922
|
+
f"Received HealthCheckRequest message {message_id} (inner_id: {inner_msg_id})"
|
820
923
|
)
|
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
924
|
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
925
|
+
# Forward to health stream for health worker subprocess to process
|
926
|
+
if REDIS_HEALTH_STREAM:
|
927
|
+
print(
|
928
|
+
f"[DEBUG] Forwarding health check to health stream: {REDIS_HEALTH_STREAM}"
|
929
|
+
)
|
930
|
+
try:
|
931
|
+
# Forward the entire message data to the health stream
|
932
|
+
health_message_data = {
|
933
|
+
"data": json.dumps(
|
934
|
+
{
|
935
|
+
"kind": inner_kind,
|
936
|
+
"id": inner_msg_id,
|
937
|
+
"content": actual_content_to_process,
|
938
|
+
"created_at": inner_created_at.isoformat(),
|
939
|
+
"return_stream": return_stream,
|
940
|
+
"user_id": user_id,
|
941
|
+
"orgs": orgs,
|
942
|
+
"handle": handle,
|
943
|
+
"adapter": adapter,
|
944
|
+
"api_key": api_key,
|
945
|
+
"original_message_id": message_id, # Include original message ID for tracking
|
946
|
+
}
|
947
|
+
)
|
948
|
+
}
|
949
|
+
|
950
|
+
print(
|
951
|
+
f"[DEBUG] Health message data to forward: {json.dumps(health_message_data, indent=2)}"
|
952
|
+
)
|
953
|
+
|
954
|
+
r.xadd(REDIS_HEALTH_STREAM, health_message_data) # type: ignore[arg-type]
|
955
|
+
print(
|
956
|
+
f"[DEBUG] Health check forwarded successfully to {REDIS_HEALTH_STREAM}"
|
957
|
+
)
|
958
|
+
logger.info(
|
959
|
+
f"Forwarded HealthCheckRequest {message_id} to health stream {REDIS_HEALTH_STREAM}"
|
960
|
+
)
|
961
|
+
|
962
|
+
except Exception as e:
|
963
|
+
print(
|
964
|
+
f"[DEBUG] ERROR forwarding health check to health stream: {e}"
|
965
|
+
)
|
966
|
+
logger.error(f"Error forwarding health check to health stream: {e}")
|
967
|
+
# Fall back to sending error response directly
|
968
|
+
_send_error_response(
|
969
|
+
message_id,
|
970
|
+
f"Failed to forward health check: {e}",
|
971
|
+
traceback.format_exc(),
|
972
|
+
return_stream,
|
973
|
+
user_id,
|
974
|
+
)
|
975
|
+
else:
|
976
|
+
print(
|
977
|
+
f"[DEBUG] No health stream configured, cannot forward health check"
|
978
|
+
)
|
979
|
+
logger.warning(
|
980
|
+
"No health stream configured for health check forwarding"
|
981
|
+
)
|
982
|
+
# Send error response since we can't process health checks
|
983
|
+
_send_error_response(
|
984
|
+
message_id,
|
985
|
+
"Health check stream not configured",
|
986
|
+
"REDIS_HEALTH_STREAM environment variable not set",
|
987
|
+
return_stream,
|
988
|
+
user_id,
|
989
|
+
)
|
990
|
+
|
991
|
+
# Acknowledge the original message since we've forwarded it
|
992
|
+
print(f"[DEBUG] Acknowledging original health check message {message_id}")
|
993
|
+
try:
|
994
|
+
assert isinstance(REDIS_STREAM, str)
|
995
|
+
assert isinstance(REDIS_CONSUMER_GROUP, str)
|
996
|
+
r.xack(REDIS_STREAM, REDIS_CONSUMER_GROUP, message_id)
|
997
|
+
print(
|
998
|
+
f"[DEBUG] Health check message {message_id} acknowledged successfully"
|
999
|
+
)
|
1000
|
+
logger.info(f"Acknowledged HealthCheckRequest message {message_id}")
|
1001
|
+
except Exception as e:
|
1002
|
+
print(f"[DEBUG] ERROR acknowledging health check message: {e}")
|
1003
|
+
logger.error(f"Error acknowledging health check message: {e}")
|
1004
|
+
|
1005
|
+
print(f"[DEBUG] *** HEALTH CHECK FORWARDING COMPLETE ***")
|
838
1006
|
return # Exit early for health checks
|
839
1007
|
# --- End Health Check Logic ---
|
840
1008
|
|
@@ -1210,19 +1378,31 @@ logger.info(
|
|
1210
1378
|
|
1211
1379
|
# Start the health check consumer subprocess
|
1212
1380
|
if REDIS_HEALTH_STREAM and REDIS_HEALTH_CONSUMER_GROUP:
|
1381
|
+
print(f"[DEBUG] === HEALTH SUBPROCESS INITIALIZATION ===")
|
1382
|
+
print(f"[DEBUG] REDIS_HEALTH_STREAM is set: {REDIS_HEALTH_STREAM}")
|
1383
|
+
print(f"[DEBUG] REDIS_HEALTH_CONSUMER_GROUP is set: {REDIS_HEALTH_CONSUMER_GROUP}")
|
1384
|
+
|
1213
1385
|
health_subprocess = start_health_check_subprocess()
|
1214
1386
|
if health_subprocess:
|
1387
|
+
print(
|
1388
|
+
f"[DEBUG] Health subprocess started successfully, starting monitor thread..."
|
1389
|
+
)
|
1215
1390
|
# Start monitoring thread for subprocess output
|
1216
1391
|
monitor_thread = threading.Thread(
|
1217
1392
|
target=monitor_health_subprocess, args=(health_subprocess,), daemon=True
|
1218
1393
|
)
|
1219
1394
|
monitor_thread.start()
|
1395
|
+
print(f"[DEBUG] Monitor thread started for health subprocess")
|
1220
1396
|
logger.info(
|
1221
1397
|
f"[Consumer] Health check subprocess for {REDIS_HEALTH_STREAM} started and monitoring thread started."
|
1222
1398
|
)
|
1223
1399
|
else:
|
1400
|
+
print(f"[DEBUG] Health subprocess failed to start")
|
1224
1401
|
logger.error("[Consumer] Failed to start health check subprocess.")
|
1225
1402
|
else:
|
1403
|
+
print(f"[DEBUG] === HEALTH SUBPROCESS NOT CONFIGURED ===")
|
1404
|
+
print(f"[DEBUG] REDIS_HEALTH_STREAM: {REDIS_HEALTH_STREAM}")
|
1405
|
+
print(f"[DEBUG] REDIS_HEALTH_CONSUMER_GROUP: {REDIS_HEALTH_CONSUMER_GROUP}")
|
1226
1406
|
logger.warning(
|
1227
1407
|
"[Consumer] Health check stream not configured. Health consumer subprocess not started."
|
1228
1408
|
)
|
@@ -1232,10 +1412,15 @@ try:
|
|
1232
1412
|
logger.debug(
|
1233
1413
|
f"[{datetime.now(timezone.utc).isoformat()}] --- Top of main loop ---"
|
1234
1414
|
) # Added log
|
1415
|
+
print(f"[DEBUG] === MAIN LOOP ITERATION START ===")
|
1235
1416
|
|
1236
1417
|
# --- Check Health Subprocess Status ---
|
1237
1418
|
if health_subprocess:
|
1238
|
-
|
1419
|
+
print(f"[DEBUG] Checking health subprocess status...")
|
1420
|
+
health_status = check_health_subprocess()
|
1421
|
+
print(f"[DEBUG] Health subprocess status check result: {health_status}")
|
1422
|
+
else:
|
1423
|
+
print(f"[DEBUG] No health subprocess to check")
|
1239
1424
|
|
1240
1425
|
# --- Check for Code Updates ---
|
1241
1426
|
if not disable_hot_reload:
|
@@ -1448,6 +1633,11 @@ try:
|
|
1448
1633
|
logger.debug(
|
1449
1634
|
f"[{datetime.now(timezone.utc).isoformat()}] Calling xreadgroup (block=5000ms)..."
|
1450
1635
|
) # Added log
|
1636
|
+
print(f"[DEBUG] About to call xreadgroup...")
|
1637
|
+
print(f"[DEBUG] Stream: {REDIS_STREAM}")
|
1638
|
+
print(f"[DEBUG] Consumer group: {REDIS_CONSUMER_GROUP}")
|
1639
|
+
print(f"[DEBUG] Consumer name: {consumer_name}")
|
1640
|
+
|
1451
1641
|
messages = r.xreadgroup(
|
1452
1642
|
REDIS_CONSUMER_GROUP,
|
1453
1643
|
consumer_name,
|
@@ -1456,10 +1646,14 @@ try:
|
|
1456
1646
|
block=5000, # Use milliseconds for block
|
1457
1647
|
)
|
1458
1648
|
|
1649
|
+
print(f"[DEBUG] xreadgroup returned: {messages}")
|
1650
|
+
print(f"[DEBUG] Messages type: {type(messages)}")
|
1651
|
+
|
1459
1652
|
if not messages:
|
1460
1653
|
logger.trace(
|
1461
1654
|
f"[{datetime.now(timezone.utc).isoformat()}] xreadgroup timed out (no new messages)."
|
1462
1655
|
) # Added log
|
1656
|
+
print(f"[DEBUG] No messages received (timeout or empty)")
|
1463
1657
|
# logger.debug("[Consumer] No new messages.") # Reduce verbosity
|
1464
1658
|
continue
|
1465
1659
|
# Removed the else block here
|
@@ -1475,6 +1669,8 @@ try:
|
|
1475
1669
|
stream_name_str, stream_messages = typed_messages[0]
|
1476
1670
|
num_msgs = len(stream_messages)
|
1477
1671
|
|
1672
|
+
print(f"[DEBUG] Processing {num_msgs} message(s) from stream {stream_name_str}")
|
1673
|
+
|
1478
1674
|
# Log reception and count before processing
|
1479
1675
|
logger.info(
|
1480
1676
|
f"[{datetime.now(timezone.utc).isoformat()}] xreadgroup returned {num_msgs} message(s). Processing..."
|
@@ -1492,8 +1688,13 @@ try:
|
|
1492
1688
|
# for k, v in msg_data_bytes_dict.items() } # No longer needed
|
1493
1689
|
# print(f"Processing message {message_id_str}") # Reduce verbosity
|
1494
1690
|
# print(f"Message data: {message_data_str_dict}") # Reduce verbosity
|
1691
|
+
print(f"[DEBUG] === PROCESSING MESSAGE {message_id_str} ===")
|
1692
|
+
print(f"[DEBUG] Message data keys: {list(message_data_str_dict.keys())}")
|
1693
|
+
|
1495
1694
|
process_message(message_id_str, message_data_str_dict)
|
1496
1695
|
|
1696
|
+
print(f"[DEBUG] === FINISHED PROCESSING MESSAGE {message_id_str} ===")
|
1697
|
+
|
1497
1698
|
except ConnectionError as e:
|
1498
1699
|
logger.error(f"Redis connection error: {e}. Reconnecting in 5s...")
|
1499
1700
|
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...")
|
@@ -13,8 +13,8 @@ nebu/containers/container.py,sha256=Mrh_gvMsTvDkj3CwpqIPzJ72IMw0gQIg64y548vq0yg,
|
|
13
13
|
nebu/containers/models.py,sha256=0j6NGy4yto-enRDh_4JH_ZTbHrLdSpuMOqNQPnIrwC4,6815
|
14
14
|
nebu/namespaces/models.py,sha256=EqUOpzhVBhvJw2P92ONDUbIgC31M9jMmcaG5vyOrsWg,497
|
15
15
|
nebu/namespaces/namespace.py,sha256=oeZyGqsIGIrppyjif1ZONsdTmqRgd9oSLFE1BChXTTE,5247
|
16
|
-
nebu/processors/consumer.py,sha256=
|
17
|
-
nebu/processors/consumer_health_worker.py,sha256=
|
16
|
+
nebu/processors/consumer.py,sha256=19f7BpNZXmeJSDmYmfX8mVFUZzd_vUI5bMI1alpDIvc,78483
|
17
|
+
nebu/processors/consumer_health_worker.py,sha256=7qT3Iu3aUoPqg0Doy-MWN7RMpoS1yXljLMaF3QikF2M,14483
|
18
18
|
nebu/processors/consumer_process_worker.py,sha256=h--eNFKaLbUayxn88mB8oGGdrU2liE1dnwm_TPlewX8,36960
|
19
19
|
nebu/processors/decorate.py,sha256=5p9pQrk_H8-Fj0UjsgSVCYx7Jk7KFuhMZtNhkKvpmkQ,61306
|
20
20
|
nebu/processors/default.py,sha256=cy4ETMdbdRGkrvbYec1o60h7mGDlGN5JsuUph0ENtDU,364
|
@@ -22,8 +22,8 @@ nebu/processors/models.py,sha256=8-TmKha2_QAnPlXcZxYjrCSPDCX7FFcMDMcHK77jK0U,422
|
|
22
22
|
nebu/processors/processor.py,sha256=APzvh4wB97pazl0SJewJEfi8sjQzQAMXZjtONNjaF_0,26081
|
23
23
|
nebu/redis/models.py,sha256=coPovAcVXnOU1Xh_fpJL4PO3QctgK9nBe5QYoqEcnxg,1230
|
24
24
|
nebu/services/service.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
25
|
-
nebu-0.1.
|
26
|
-
nebu-0.1.
|
27
|
-
nebu-0.1.
|
28
|
-
nebu-0.1.
|
29
|
-
nebu-0.1.
|
25
|
+
nebu-0.1.120.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
26
|
+
nebu-0.1.120.dist-info/METADATA,sha256=SKNf4PIhc1Stx6bHBxGQ7CbZCNsK34FntOaHgJiZRQ0,1798
|
27
|
+
nebu-0.1.120.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
28
|
+
nebu-0.1.120.dist-info/top_level.txt,sha256=uLIbEKJeGSHWOAJN5S0i5XBGwybALlF9bYoB1UhdEgQ,5
|
29
|
+
nebu-0.1.120.dist-info/RECORD,,
|
File without changes
|
File without changes
|