Skip to content

Sync

The fire-and-forget problem

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 methods wrap fire-and-forget START/STOP commands with a polling loop that issues DISPLAY *STATUS until the object reaches a stable state or the timeout expires.

Each call returns a sync result describing what happened:

  • Operation: STARTED, STOPPED, or RESTARTED.
  • Polls: Number of status polls issued.
  • Elapsed time: Wall-clock seconds from command to confirmation.

Configuration

Polling behaviour is controlled by two parameters:

Parameter Default Description
Timeout 30 seconds Maximum wait before raising a timeout error
Poll interval 1 second Seconds between status polls

Timeout on expiry

If the object does not reach the target state within the configured timeout, a timeout error is raised. The error includes the object name, the attempted operation, and the elapsed time — enabling callers to handle partial progress or escalate to an operator.

Restart convenience

The restart methods perform a synchronous stop followed by a synchronous start. Each phase gets the full timeout independently — the worst case is twice the configured timeout.

The returned result reports total polls and total elapsed time across both phases.

Available object types

The sync pattern is available for object types that have start/stop semantics:

Object type START/STOP qualifier Status qualifier
Channel CHANNEL CHSTATUS
Listener LISTENER LSSTATUS
Service SERVICE SVSTATUS

Each object type supports start, stop, and restart — nine methods in total.

Channel stop edge case

When a channel stops, its CHSTATUS record may disappear entirely — the DISPLAY CHSTATUS response returns no rows. The sync methods treat an empty status result as successfully stopped for channels. Listener and service status records are always present, so empty results are not treated as stopped for those object types.

Status detection

The polling loop checks the STATUS attribute in the DISPLAY *STATUS response. The target values are:

  • Start: RUNNING
  • Stop: STOPPED

The status key is checked using both the mapped developer-friendly name and the raw MQSC name, so polling works correctly regardless of whether attribute mapping is enabled or disabled.

SyncConfig

SyncConfig is an immutable value object (Data.define):

Field Type Default Description
timeout_seconds Float 30.0 Maximum wait time
poll_interval_seconds Float 1.0 Delay between polls

SyncResult

SyncResult is an immutable value object (Data.define):

Field Type Description
operation Symbol :started, :stopped, or :restarted
polls Integer Number of status polls performed
elapsed_seconds Float Total elapsed time

Basic usage

# Start a channel and wait for it to reach RUNNING
result = session.start_channel_sync('MY.CHANNEL')

puts result.operation      # :started
puts result.polls          # number of status polls
puts result.elapsed_seconds

Custom configuration

config = MQ::REST::Admin::SyncConfig.new(
  timeout_seconds: 60.0,
  poll_interval_seconds: 2.0
)

result = session.start_channel_sync('MY.CHANNEL', config: config)

Restart convenience methods

Each object type has a restart method that stops, waits for stopped, then starts and waits for running:

result = session.restart_channel('MY.CHANNEL')
puts result.operation      # :restarted
puts result.polls          # total polls across stop + start

Timeout handling

If the operation doesn't complete within the timeout, a TimeoutError is raised:

begin
  session.start_channel_sync('MY.CHANNEL',
    config: MQ::REST::Admin::SyncConfig.new(timeout_seconds: 5.0)
  )
rescue MQ::REST::Admin::TimeoutError => e
  puts "#{e.operation} timed out for #{e.name} after #{e.elapsed}s"
end

Provisioning example

config = MQ::REST::Admin::SyncConfig.new(timeout_seconds: 60.0)

# Ensure queues exist
session.ensure_qlocal('APP.REQUESTS', 'max_queue_depth' => '50000')
session.ensure_qlocal('APP.RESPONSES', 'max_queue_depth' => '50000')

# Start listener
session.start_listener_sync('TCP.LISTENER', config: config)

puts 'Environment provisioned'

Rolling restart example

config = MQ::REST::Admin::SyncConfig.new(timeout_seconds: 30.0)

channels = session.display_channel(name: 'APP.*')
channels.each do |ch|
  name = ch['channel_name']
  puts "Restarting #{name}..."
  result = session.restart_channel(name, config: config)
  puts "  #{result.operation} in #{result.elapsed_seconds.round(1)}s (#{result.polls} polls)"
end