Synchronous start/stop/restart¶
The problem with fire-and-forget¶
All MQSC START and STOP commands are fire-and-forget — they return
immediately without waiting for the object to reach its target state.
In practice, tooling that provisions infrastructure needs to wait until
a channel is RUNNING or a listener is STOPPED before proceeding to
the next step. Writing polling loops by hand is error-prone and
clutters business logic with retry mechanics.
The sync pattern¶
The *_sync and restart_* methods wrap the fire-and-forget commands
with a polling loop that issues DISPLAY *STATUS until the object
reaches a stable state or the timeout expires.
Each call returns a SyncResult describing what happened:
from pymqrest import SyncConfig, SyncOperation, SyncResult
class SyncOperation(enum.Enum):
STARTED = "started" # Object confirmed running
STOPPED = "stopped" # Object confirmed stopped
RESTARTED = "restarted" # Stop-then-start completed
@dataclass(frozen=True)
class SyncResult:
operation: SyncOperation
polls: int # Number of status polls issued
elapsed_seconds: float # Wall-clock time from command to confirmation
Polling is controlled by a SyncConfig dataclass:
@dataclass(frozen=True)
class SyncConfig:
timeout_seconds: float = 30.0 # Max wait before raising
poll_interval_seconds: float = 1.0 # Seconds between polls
If the object does not reach the target state within the timeout,
MQRESTTimeoutError is raised.
Basic usage¶
from pymqrest import MQRESTSession, SyncConfig, SyncOperation
from pymqrest.auth import BasicAuth
session = MQRESTSession(
rest_base_url="https://localhost:9443/ibmmq/rest/v2",
qmgr_name="QM1",
credentials=BasicAuth("mqadmin", "mqadmin"),
verify_tls=False,
)
# Start a channel and wait until it is RUNNING
result = session.start_channel_sync("TO.PARTNER")
assert result.operation is SyncOperation.STARTED
print(f"Channel running after {result.polls} poll(s), {result.elapsed_seconds:.1f}s")
# Stop a listener and wait until it is STOPPED
result = session.stop_listener_sync("TCP.LISTENER")
assert result.operation is SyncOperation.STOPPED
Restart convenience¶
The restart_* methods perform a synchronous stop followed by a
synchronous start. Each phase gets the full timeout independently —
worst case is 2x the configured timeout.
The returned SyncResult reports total polls and total elapsed
time across both phases:
result = session.restart_channel("TO.PARTNER")
assert result.operation is SyncOperation.RESTARTED
print(f"Restarted in {result.elapsed_seconds:.1f}s ({result.polls} total polls)")
Custom timeout and poll interval¶
Pass a SyncConfig to override the defaults:
from pymqrest import SyncConfig
# Aggressive polling for fast local development
fast = SyncConfig(timeout_seconds=10.0, poll_interval_seconds=0.25)
result = session.start_service_sync("MY.SVC", config=fast)
# Patient polling for remote queue managers
patient = SyncConfig(timeout_seconds=120.0, poll_interval_seconds=5.0)
result = session.start_channel_sync("REMOTE.CHL", config=patient)
Timeout handling¶
When the timeout expires, MQRESTTimeoutError is raised with
diagnostic attributes:
from pymqrest import MQRESTTimeoutError, SyncConfig
try:
session.start_channel_sync(
"BROKEN.CHL",
config=SyncConfig(timeout_seconds=15.0),
)
except MQRESTTimeoutError as err:
print(f"Object: {err.name}") # "BROKEN.CHL"
print(f"Operation: {err.operation}") # "start"
print(f"Elapsed: {err.elapsed:.1f}s") # 15.0
MQRESTTimeoutError inherits from MQRESTError, so existing
except MQRESTError handlers will catch it.
Available methods¶
| Method | Operation | START/STOP qualifier | Status qualifier |
|---|---|---|---|
start_channel_sync() |
Start | CHANNEL |
CHSTATUS |
stop_channel_sync() |
Stop | CHANNEL |
CHSTATUS |
restart_channel() |
Restart | CHANNEL |
CHSTATUS |
start_listener_sync() |
Start | LISTENER |
LSSTATUS |
stop_listener_sync() |
Stop | LISTENER |
LSSTATUS |
restart_listener() |
Restart | LISTENER |
LSSTATUS |
start_service_sync() |
Start | SERVICE |
SVSTATUS |
stop_service_sync() |
Stop | SERVICE |
SVSTATUS |
restart_service() |
Restart | SERVICE |
SVSTATUS |
All methods share the same signature:
The config parameter is keyword-only for API clarity.
Status detection¶
The polling loop checks the STATUS attribute in the DISPLAY *STATUS
response. The target values are:
- Start:
RUNNING - Stop:
STOPPED
Channel stop edge case¶
When a channel stops, its CHSTATUS record may disappear entirely
(the DISPLAY CHSTATUS response returns no rows). The channel sync
methods treat an empty status result as successfully stopped. Listener
and service status records are always present, so empty results are not
treated as stopped for those object types.
Attribute mapping¶
The sync methods call _mqsc_command internally, so they participate
in the same mapping pipeline as all other
command methods. The status key is checked using both the mapped
snake_case name and the raw MQSC name, so polling works correctly
regardless of whether mapping is enabled or disabled.
Provisioning example¶
The sync methods pair naturally with the ensure methods for end-to-end provisioning:
from pymqrest import SyncConfig
config = SyncConfig(timeout_seconds=60.0)
# Ensure listeners exist for application and admin traffic
session.ensure_listener("APP.LISTENER", request_parameters={
"transport_type": "TCP",
"port": 1415,
"start_mode": "MQSVC_CONTROL_Q_MGR",
})
session.ensure_listener("ADMIN.LISTENER", request_parameters={
"transport_type": "TCP",
"port": 1416,
"start_mode": "MQSVC_CONTROL_Q_MGR",
})
# Start them synchronously
session.start_listener_sync("APP.LISTENER", config=config)
session.start_listener_sync("ADMIN.LISTENER", config=config)
print("Listeners ready")
Rolling restart example¶
Restart all listeners with error handling — useful when a queue manager serves multiple TCP ports for different client populations:
from pymqrest import MQRESTTimeoutError, SyncConfig
listeners = ["APP.LISTENER", "ADMIN.LISTENER", "PARTNER.LISTENER"]
config = SyncConfig(timeout_seconds=30.0, poll_interval_seconds=2.0)
for name in listeners:
try:
result = session.restart_listener(name, config=config)
print(f"{name}: restarted in {result.elapsed_seconds:.1f}s")
except MQRESTTimeoutError as err:
print(f"{name}: timed out after {err.elapsed:.1f}s")