github.com/lirm/aeron-go@v0.0.0-20230415210743-920325491dc4/archive/control.go (about)

     1  // Copyright (C) 2021-2022 Talos, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package archive
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"time"
    21  
    22  	"github.com/lirm/aeron-go/aeron"
    23  	"github.com/lirm/aeron-go/aeron/atomic"
    24  	"github.com/lirm/aeron-go/aeron/logbuffer"
    25  	"github.com/lirm/aeron-go/aeron/logbuffer/term"
    26  	"github.com/lirm/aeron-go/archive/codecs"
    27  )
    28  
    29  const controlFragmentLimit = 10
    30  
    31  // Control contains everything required for the archive subscription/response side
    32  type Control struct {
    33  	Subscription *aeron.Subscription
    34  	State        controlState
    35  
    36  	// Polling results
    37  	Results ControlResults
    38  
    39  	archive           *Archive // link to parent
    40  	fragmentAssembler *aeron.ControlledFragmentAssembler
    41  }
    42  
    43  // ControlResults for holding state over a Control request/response
    44  // The polling mechanism is not parameterized so we need to set state for the results as we go
    45  // These pieces are filled out by various ResponsePollers which will set IsPollComplete to true
    46  type ControlResults struct {
    47  	CorrelationId                    int64
    48  	ControlResponse                  *codecs.ControlResponse
    49  	RecordingDescriptors             []*codecs.RecordingDescriptor
    50  	RecordingSubscriptionDescriptors []*codecs.RecordingSubscriptionDescriptor
    51  	IsPollComplete                   bool
    52  	FragmentsReceived                int
    53  	ErrorResponse                    error // Used by PollForErrorResponse
    54  }
    55  
    56  // PollContext contains the information we'll need in the image Poll()
    57  // callback to match against our request or for async events to invoke
    58  // the appropriate listener
    59  type PollContext struct {
    60  	control       *Control
    61  	correlationID int64
    62  }
    63  
    64  // Archive Connection State used internally for connection establishment
    65  const (
    66  	ControlStateError              = -1
    67  	ControlStateNew                = iota
    68  	ControlStateConnectRequestSent = iota
    69  	ControlStateChallenged         = iota
    70  	ControlStateConnectRequestOk   = iota
    71  	ControlStateConnected          = iota
    72  	ControlStateTimedOut           = iota
    73  )
    74  
    75  // Used internally to handle connection state
    76  type controlState struct {
    77  	state int
    78  	err   error
    79  }
    80  
    81  // CodecIds stops us allocating every object when we need only one
    82  // Arguably SBE should give us a static value
    83  type CodecIds struct {
    84  	controlResponse                 uint16
    85  	challenge                       uint16
    86  	recordingDescriptor             uint16
    87  	recordingSubscriptionDescriptor uint16
    88  	recordingSignalEvent            uint16
    89  	recordingStarted                uint16
    90  	recordingProgress               uint16
    91  	recordingStopped                uint16
    92  }
    93  
    94  var codecIds CodecIds
    95  
    96  func init() {
    97  	var controlResponse codecs.ControlResponse
    98  	var challenge codecs.Challenge
    99  	var recordingDescriptor codecs.RecordingDescriptor
   100  	var recordingSubscriptionDescriptor codecs.RecordingSubscriptionDescriptor
   101  	var recordingSignalEvent codecs.RecordingSignalEvent
   102  	var recordingStarted = new(codecs.RecordingStarted)
   103  	var recordingProgress = new(codecs.RecordingProgress)
   104  	var recordingStopped = new(codecs.RecordingStopped)
   105  
   106  	codecIds.controlResponse = controlResponse.SbeTemplateId()
   107  	codecIds.challenge = challenge.SbeTemplateId()
   108  	codecIds.recordingDescriptor = recordingDescriptor.SbeTemplateId()
   109  	codecIds.recordingSubscriptionDescriptor = recordingSubscriptionDescriptor.SbeTemplateId()
   110  	codecIds.recordingSignalEvent = recordingSignalEvent.SbeTemplateId()
   111  	codecIds.recordingStarted = recordingStarted.SbeTemplateId()
   112  	codecIds.recordingProgress = recordingProgress.SbeTemplateId()
   113  	codecIds.recordingStopped = recordingStopped.SbeTemplateId()
   114  }
   115  
   116  func controlFragmentHandler(context interface{}, buffer *atomic.Buffer, offset int32, length int32, header *logbuffer.Header) {
   117  	pollContext, ok := context.(*PollContext)
   118  	if !ok {
   119  		logger.Errorf("context conversion failed")
   120  		return
   121  	}
   122  
   123  	logger.Debugf("controlFragmentHandler: correlationID:%d offset:%d length:%d header:%#v", pollContext.correlationID, offset, length, header)
   124  	var hdr codecs.SbeGoMessageHeader
   125  
   126  	buf := new(bytes.Buffer)
   127  	buffer.WriteBytes(buf, offset, length)
   128  
   129  	marshaller := codecs.NewSbeGoMarshaller()
   130  	if err := hdr.Decode(marshaller, buf); err != nil {
   131  		// Not much to be done here as we can't really tell what went wrong
   132  		err2 := fmt.Errorf("controlFragmentHandler() failed to decode control message header: %w", err)
   133  		// Call the global error handler, ugly but it's all we've got
   134  		if pollContext.control.archive.Listeners.ErrorListener != nil {
   135  			pollContext.control.archive.Listeners.ErrorListener(err2)
   136  		}
   137  		return
   138  	}
   139  
   140  	// Look up our control
   141  	c, ok := correlations.Load(pollContext.correlationID)
   142  	if !ok {
   143  		// something has gone horribly wrong and we can't correlate
   144  		if pollContext.control.archive.Listeners.ErrorListener != nil {
   145  			pollContext.control.archive.Listeners.ErrorListener(fmt.Errorf("failed to locate control via correlationID %d", pollContext.correlationID))
   146  		}
   147  		logger.Debugf("failed to locate control via correlationID %d", pollContext.correlationID)
   148  		return
   149  	}
   150  	control := c.(*Control)
   151  
   152  	switch hdr.TemplateId {
   153  	case codecIds.controlResponse:
   154  		var controlResponse = new(codecs.ControlResponse)
   155  		logger.Debugf("controlFragmentHandler/controlResponse: Received controlResponse: length %d", buf.Len())
   156  		if err := controlResponse.Decode(marshaller, buf, hdr.Version, hdr.BlockLength, rangeChecking); err != nil {
   157  			// Not much to be done here as we can't see what's gone wrong
   158  			err2 := fmt.Errorf("controlFragmentHandler failed to decode control response:%w", err)
   159  			// Call the global error handler, ugly but it's all we've got
   160  			if pollContext.control.archive.Listeners.ErrorListener != nil {
   161  				pollContext.control.archive.Listeners.ErrorListener(err2)
   162  			}
   163  			return
   164  		}
   165  
   166  		// Check this was for us
   167  		if controlResponse.ControlSessionId == control.archive.SessionID && controlResponse.CorrelationId == pollContext.correlationID {
   168  			// Set our state to let the caller of Poll() which triggered this know they have something
   169  			// We're basically finished so prepare our OOB return values and log some info if we can
   170  			logger.Debugf("controlFragmentHandler/controlResponse: received for sessionID:%d, correlationID:%d", controlResponse.ControlSessionId, controlResponse.CorrelationId)
   171  			control.Results.ControlResponse = controlResponse
   172  			control.Results.IsPollComplete = true
   173  		} else {
   174  			logger.Debugf("controlFragmentHandler/controlResponse ignoring sessionID:%d, correlationID:%d", controlResponse.ControlSessionId, controlResponse.CorrelationId)
   175  		}
   176  
   177  	case codecIds.recordingSignalEvent:
   178  		var recordingSignalEvent = new(codecs.RecordingSignalEvent)
   179  
   180  		if err := recordingSignalEvent.Decode(marshaller, buf, hdr.Version, hdr.BlockLength, rangeChecking); err != nil {
   181  			// Not much to be done here as we can't really tell what went wrong
   182  			err2 := fmt.Errorf("ControlFragmentHandler failed to decode recording signal: %w", err)
   183  			if pollContext.control.archive.Listeners.ErrorListener != nil {
   184  				pollContext.control.archive.Listeners.ErrorListener(err2)
   185  			}
   186  		}
   187  		if pollContext.control.archive.Listeners.RecordingSignalListener != nil {
   188  			pollContext.control.archive.Listeners.RecordingSignalListener(recordingSignalEvent)
   189  		}
   190  
   191  	// These can happen when testing/reconnecting or if multiple clients are on the same channel/stream
   192  	case codecIds.recordingDescriptor:
   193  		logger.Debugf("controlFragmentHandler: ignoring RecordingDescriptor type %d", hdr.TemplateId)
   194  	case codecIds.recordingSubscriptionDescriptor:
   195  		logger.Debugf("controlFragmentHandler: ignoring RecordingSubscriptionDescriptor type %d", hdr.TemplateId)
   196  
   197  	default:
   198  		// This can happen when testing/adding new functionality
   199  		fmt.Printf("controlFragmentHandler: Unexpected message type %d\n", hdr.TemplateId)
   200  	}
   201  }
   202  
   203  // ConnectionControlFragmentHandler is the connection handling specific fragment handler.
   204  // This mechanism only alows us to pass results back via global state which we do in control.State
   205  func ConnectionControlFragmentHandler(context *PollContext, buffer *atomic.Buffer, offset int32, length int32, header *logbuffer.Header) {
   206  	logger.Debugf("ConnectionControlFragmentHandler: correlationID:%d offset:%d length: %d header: %#v", context.correlationID, offset, length, header)
   207  
   208  	var hdr codecs.SbeGoMessageHeader
   209  
   210  	buf := new(bytes.Buffer)
   211  	buffer.WriteBytes(buf, offset, length)
   212  
   213  	marshaller := codecs.NewSbeGoMarshaller()
   214  	if err := hdr.Decode(marshaller, buf); err != nil {
   215  		// Not much to be done here as we can't correlate
   216  		err2 := fmt.Errorf("ConnectionControlFragmentHandler() failed to decode control message header: %w", err)
   217  		// Call the global error handler, ugly but it's all we've got
   218  		if context.control.archive.Listeners.ErrorListener != nil {
   219  			context.control.archive.Listeners.ErrorListener(err2)
   220  		}
   221  	}
   222  
   223  	switch hdr.TemplateId {
   224  	case codecIds.controlResponse:
   225  		var controlResponse = new(codecs.ControlResponse)
   226  		logger.Debugf("Received controlResponse: length %d", buf.Len())
   227  		if err := controlResponse.Decode(marshaller, buf, hdr.Version, hdr.BlockLength, rangeChecking); err != nil {
   228  			// Not much to be done here as we can't correlate
   229  			err2 := fmt.Errorf("ConnectionControlFragmentHandler failed to decode control response: %w", err)
   230  			if context.control.archive.Listeners.ErrorListener != nil {
   231  				context.control.archive.Listeners.ErrorListener(err2)
   232  			}
   233  			logger.Debugf("ConnectionControlFragmentHandler failed to decode control response: %w", err)
   234  			return
   235  		}
   236  
   237  		// Look this message up
   238  		c, ok := correlations.Load(controlResponse.CorrelationId)
   239  		if !ok {
   240  			logger.Debugf("connectionControlFragmentHandler/controlResponse: ignoring correlationID=%d [%s]\n%#v", controlResponse.CorrelationId, string(controlResponse.ErrorMessage), controlResponse)
   241  			return
   242  		}
   243  		control := c.(*Control)
   244  
   245  		// Check this was for us
   246  		if controlResponse.CorrelationId == context.correlationID {
   247  			// Check result
   248  			if controlResponse.Code != codecs.ControlResponseCode.OK {
   249  				control.State.state = ControlStateError
   250  				control.State.err = fmt.Errorf("Control Response failure: %s", controlResponse.ErrorMessage)
   251  				if context.control.archive.Listeners.ErrorListener != nil {
   252  					context.control.archive.Listeners.ErrorListener(control.State.err)
   253  				}
   254  				return
   255  			}
   256  
   257  			// assert state change
   258  			if control.State.state != ControlStateConnectRequestSent {
   259  				control.State.state = ControlStateError
   260  				control.State.err = fmt.Errorf("Control Response not expecting response")
   261  				if context.control.archive.Listeners.ErrorListener != nil {
   262  					context.control.archive.Listeners.ErrorListener(control.State.err)
   263  				}
   264  			}
   265  
   266  			// Looking good, so update state and store the SessionID
   267  			control.State.state = ControlStateConnected
   268  			control.State.err = nil
   269  			control.archive.SessionID = controlResponse.ControlSessionId
   270  		} else {
   271  			// It's conceivable if the same application is making concurrent connection attempts using
   272  			// the same channel/stream that we can reach here which is our parent's problem
   273  			logger.Debugf("connectionControlFragmentHandler/controlResponse: ignoring correlationID=%d", controlResponse.CorrelationId)
   274  		}
   275  
   276  	case codecIds.challenge:
   277  		var challenge = new(codecs.Challenge)
   278  
   279  		if err := challenge.Decode(marshaller, buf, hdr.Version, hdr.BlockLength, rangeChecking); err != nil {
   280  			// Not much to be done here as we can't correlate
   281  			err2 := fmt.Errorf("ControlFragmentHandler failed to decode challenge: %w", err)
   282  			if context.control.archive.Listeners.ErrorListener != nil {
   283  				context.control.archive.Listeners.ErrorListener(err2)
   284  			}
   285  		}
   286  
   287  		logger.Infof("ControlFragmentHandler: challenge:%s, session:%d, correlationID:%d", challenge.EncodedChallenge, challenge.ControlSessionId, challenge.CorrelationId)
   288  
   289  		// Look this message up
   290  		c, ok := correlations.Load(challenge.CorrelationId)
   291  		if !ok {
   292  			logger.Debugf("connectionControlFragmentHandler/controlResponse: ignoring correlationID=%d", challenge.CorrelationId)
   293  			return
   294  		}
   295  		control := c.(*Control)
   296  
   297  		// Check this was for us
   298  		if challenge.CorrelationId == context.correlationID {
   299  
   300  			// Check the challenge is expected iff our option for this is not nil
   301  			if control.archive.Options.AuthChallenge != nil {
   302  				if !bytes.Equal(control.archive.Options.AuthChallenge, challenge.EncodedChallenge) {
   303  					control.State.err = fmt.Errorf("ChallengeResponse Unexpected: expected:%v received:%v", control.archive.Options.AuthChallenge, challenge.EncodedChallenge)
   304  					return
   305  				}
   306  			}
   307  
   308  			// Send the response
   309  			// Looking good, so update state and store the SessionID
   310  			control.State.state = ControlStateChallenged
   311  			control.State.err = nil
   312  			control.archive.SessionID = challenge.ControlSessionId
   313  			control.archive.Proxy.ChallengeResponse(challenge.CorrelationId, control.archive.Options.AuthResponse)
   314  		} else {
   315  			// It's conceivable if the same application is making concurrent connection attempts using
   316  			// the same channel/stream that we can reach here which is our parent's problem
   317  			logger.Debugf("connectionControlFragmentHandler/challengr: ignoring correlationID=%d", challenge.CorrelationId)
   318  		}
   319  
   320  	// These can happen when testing/reconnecting or if multiple clients are on the same channel/stream
   321  	case codecIds.recordingDescriptor:
   322  		logger.Debugf("connectionControlFragmentHandler: ignoring RecordingDescriptor type %d", hdr.TemplateId)
   323  	case codecIds.recordingSubscriptionDescriptor:
   324  		logger.Debugf("connectionControlFragmentHandler: ignoring RecordingSubscriptionDescriptor type %d", hdr.TemplateId)
   325  	case codecIds.recordingSignalEvent:
   326  		logger.Debugf("connectionControlFragmentHandler: ignoring recordingSignalEvent type %d", hdr.TemplateId)
   327  
   328  	default:
   329  		fmt.Printf("ConnectionControlFragmentHandler: Insert decoder for type: %d", hdr.TemplateId)
   330  	}
   331  }
   332  
   333  // PollForErrorResponse polls the response stream for errors or async events.
   334  //
   335  // It will continue until it either receives an error or the queue is empty.
   336  //
   337  // If any control messages are present then they will be discarded so this
   338  // call should not be used unless there are no outstanding operations.
   339  //
   340  // This may be used to check for errors, to dispatch async events, and
   341  // to catch up on messages not for this session if for example the
   342  // same channel and stream are in use by other sessions.
   343  //
   344  // Returns an error if we detect an archive operation in progress
   345  // and a count of how many messages were consumed
   346  func (control *Control) PollForErrorResponse() (int, error) {
   347  
   348  	logger.Debugf("PollForErrorResponse(%d)", control.archive.SessionID)
   349  	context := PollContext{control, 0}
   350  	received := 0
   351  
   352  	// Poll for async events, errors etc until the queue is drained
   353  	for {
   354  		ret := control.poll(
   355  			func(buf *atomic.Buffer, offset int32, length int32, header *logbuffer.Header) {
   356  				errorResponseFragmentHandler(&context, buf, offset, length, header)
   357  			}, 1)
   358  		received += ret
   359  
   360  		// If we received a response with an error then return it
   361  		if control.Results.ErrorResponse != nil {
   362  			return received, control.Results.ErrorResponse
   363  		}
   364  
   365  		// If we polled and did nothing then return
   366  		if ret == 0 {
   367  			return received, nil
   368  		}
   369  	}
   370  
   371  	return 0, nil // Should not happen
   372  }
   373  
   374  // errorResponseFragmentHandler is used to check for errors and async events on an idle control
   375  // session. Essentially:
   376  //
   377  //	ignore messages not on our session ID
   378  //	process recordingSignalEvents
   379  //	Log a warning if we have interrupted a synchronous event
   380  func errorResponseFragmentHandler(context interface{}, buffer *atomic.Buffer, offset int32, length int32, header *logbuffer.Header) {
   381  	pollContext, ok := context.(*PollContext)
   382  	if !ok {
   383  		logger.Errorf("context conversion failed")
   384  		return
   385  	}
   386  
   387  	logger.Debugf("errorResponseFragmentHandler: offset:%d length: %d", offset, length)
   388  
   389  	var hdr codecs.SbeGoMessageHeader
   390  
   391  	buf := new(bytes.Buffer)
   392  	buffer.WriteBytes(buf, offset, length)
   393  
   394  	marshaller := codecs.NewSbeGoMarshaller()
   395  	if err := hdr.Decode(marshaller, buf); err != nil {
   396  		// Not much to be done here as we can't correlate
   397  		err2 := fmt.Errorf("ConnectionControlFragmentHandler() failed to decode control message header: %w", err)
   398  		// Call the global error handler, ugly, but it's all we've got
   399  		if pollContext.control.archive.Listeners.ErrorListener != nil {
   400  			pollContext.control.archive.Listeners.ErrorListener(err2)
   401  		}
   402  	}
   403  
   404  	switch hdr.TemplateId {
   405  	case codecIds.controlResponse:
   406  		var controlResponse = new(codecs.ControlResponse)
   407  		logger.Debugf("controlFragmentHandler/controlResponse: Received controlResponse")
   408  		if err := controlResponse.Decode(marshaller, buf, hdr.Version, hdr.BlockLength, rangeChecking); err != nil {
   409  			// Not much to be done here as we can't see what's gone wrong
   410  			err2 := fmt.Errorf("errorResponseFragmentHandler failed to decode control response:%w", err)
   411  			// Call the global error handler, ugly, but it's all we've got
   412  			if pollContext.control.archive.Listeners.ErrorListener != nil {
   413  				pollContext.control.archive.Listeners.ErrorListener(err2)
   414  			}
   415  			return
   416  		}
   417  
   418  		// If this was for us then check for errors
   419  		if controlResponse.ControlSessionId == pollContext.control.archive.SessionID {
   420  			if controlResponse.Code == codecs.ControlResponseCode.ERROR {
   421  				pollContext.control.Results.ErrorResponse = fmt.Errorf("PollForErrorResponse received a ControlResponse (correlationId:%d Code:ERROR error=\"%s\"", controlResponse.CorrelationId, controlResponse.ErrorMessage)
   422  			}
   423  		}
   424  		return
   425  
   426  	case codecIds.challenge:
   427  		var challenge = new(codecs.Challenge)
   428  
   429  		if err := challenge.Decode(marshaller, buf, hdr.Version, hdr.BlockLength, rangeChecking); err != nil {
   430  			// Not much to be done here as we can't correlate
   431  			err2 := fmt.Errorf("errorResponseFragmentHandler failed to decode challenge: %w", err)
   432  			if pollContext.control.archive.Listeners.ErrorListener != nil {
   433  				pollContext.control.archive.Listeners.ErrorListener(err2)
   434  			}
   435  		}
   436  
   437  		// If this was for us then that's bad
   438  		if challenge.ControlSessionId == pollContext.control.archive.SessionID {
   439  			pollContext.control.Results.ErrorResponse = fmt.Errorf("Received and ignoring challenge  (correlationID:%d). EerrorResponse should not be called on in parallel with sync operations", challenge.CorrelationId)
   440  			logger.Warning(pollContext.control.Results.ErrorResponse)
   441  			return
   442  		}
   443  
   444  	case codecIds.recordingDescriptor:
   445  		var rd = new(codecs.RecordingDescriptor)
   446  
   447  		if err := rd.Decode(marshaller, buf, hdr.Version, hdr.BlockLength, rangeChecking); err != nil {
   448  			// Not much to be done here as we can't correlate
   449  			err2 := fmt.Errorf("errorResponseFragmentHandler failed to decode recordingSubscription: %w", err)
   450  			if pollContext.control.archive.Listeners.ErrorListener != nil {
   451  				pollContext.control.archive.Listeners.ErrorListener(err2)
   452  			}
   453  		}
   454  
   455  		// If this was for us then that's bad
   456  		if rd.ControlSessionId == pollContext.control.archive.SessionID {
   457  			pollContext.control.Results.ErrorResponse = fmt.Errorf("Received and ignoring recordingDescriptor (correlationID:%d). ErrorResponse should not be called on in parallel with sync operations", rd.CorrelationId)
   458  			logger.Warning(pollContext.control.Results.ErrorResponse)
   459  			return
   460  		}
   461  
   462  	case codecIds.recordingSubscriptionDescriptor:
   463  		var rsd = new(codecs.RecordingSubscriptionDescriptor)
   464  
   465  		if err := rsd.Decode(marshaller, buf, hdr.Version, hdr.BlockLength, rangeChecking); err != nil {
   466  			// Not much to be done here as we can't correlate
   467  			err2 := fmt.Errorf("errorResponseFragmentHandler failed to decode recordingSubscription: %w", err)
   468  			if pollContext.control.archive.Listeners.ErrorListener != nil {
   469  				pollContext.control.archive.Listeners.ErrorListener(err2)
   470  			}
   471  		}
   472  
   473  		// If this was for us then that's bad
   474  		if rsd.ControlSessionId == pollContext.control.archive.SessionID {
   475  			pollContext.control.Results.ErrorResponse = fmt.Errorf("Received and ignoring recordingsubscriptionDescriptor (correlationID:%d). ErrorResponse should not be called on in parallel with sync operations", rsd.CorrelationId)
   476  			logger.Warning(pollContext.control.Results.ErrorResponse)
   477  		}
   478  
   479  	case codecIds.recordingSignalEvent:
   480  		var rse = new(codecs.RecordingSignalEvent)
   481  
   482  		if err := rse.Decode(marshaller, buf, hdr.Version, hdr.BlockLength, rangeChecking); err != nil {
   483  			// Not much to be done here as we can't really tell what went wrong
   484  			err2 := fmt.Errorf("errorResponseFragmentHandler failed to decode recording signal: %w", err)
   485  			if pollContext.control.archive.Listeners.ErrorListener != nil {
   486  				pollContext.control.archive.Listeners.ErrorListener(err2)
   487  			}
   488  		}
   489  		if pollContext.control.archive.Listeners.RecordingSignalListener != nil {
   490  			pollContext.control.archive.Listeners.RecordingSignalListener(rse)
   491  		}
   492  
   493  		if rse.ControlSessionId == pollContext.control.archive.SessionID {
   494  			// We can call the async callback if it exists
   495  			if pollContext.control.archive.Listeners.RecordingSignalListener != nil {
   496  				pollContext.control.archive.Listeners.RecordingSignalListener(rse)
   497  			}
   498  		}
   499  
   500  	default:
   501  		fmt.Printf("errorResponseFragmentHandler: Insert decoder for type: %d", hdr.TemplateId)
   502  	}
   503  }
   504  
   505  // poll provides the control response poller using local state to pass
   506  // back data from the underlying subscription
   507  func (control *Control) poll(handler term.FragmentHandler, fragmentLimit int) int {
   508  
   509  	// Update our globals in case they've changed so we use the current state in our callback
   510  	rangeChecking = control.archive.Options.RangeChecking
   511  
   512  	control.Results.ControlResponse = nil  // Clear old results
   513  	control.Results.IsPollComplete = false // Clear completion flag
   514  
   515  	return control.Subscription.Poll(handler, fragmentLimit)
   516  }
   517  
   518  // Poll for control response events. Returns number of fragments read during the operation.
   519  // Zero if no events are available.
   520  func (control *Control) Poll() (workCount int) {
   521  	if control.Results.IsPollComplete {
   522  		// Update our globals in case they've changed so we use the current state in our callback
   523  		rangeChecking = control.archive.Options.RangeChecking
   524  		control.Results = ControlResults{}
   525  	}
   526  
   527  	return control.Subscription.ControlledPoll(control.fragmentAssembler.OnFragment, controlFragmentLimit)
   528  }
   529  
   530  func (control *Control) onFragment(
   531  	buffer *atomic.Buffer,
   532  	offset int32,
   533  	length int32,
   534  	header *logbuffer.Header,
   535  ) term.ControlledPollAction {
   536  
   537  	if control.Results.IsPollComplete {
   538  		return term.ControlledPollActionAbort
   539  	}
   540  
   541  	var hdr codecs.SbeGoMessageHeader
   542  
   543  	buf := new(bytes.Buffer)
   544  	buffer.WriteBytes(buf, offset, length)
   545  
   546  	marshaller := codecs.NewSbeGoMarshaller()
   547  	if err := hdr.Decode(marshaller, buf); err != nil {
   548  		// Not much to be done here as we can't correlate
   549  		err = fmt.Errorf("DescriptorFragmentHandler() failed to decode control message header: %w", err)
   550  		if control.archive.Listeners.ErrorListener != nil {
   551  			control.archive.Listeners.ErrorListener(err)
   552  		}
   553  		return term.ControlledPollActionContinue
   554  	}
   555  
   556  	switch hdr.TemplateId {
   557  	case codecIds.controlResponse:
   558  		var controlResponse = new(codecs.ControlResponse)
   559  		logger.Debugf("Received controlResponse: length %d", buf.Len())
   560  		if err := controlResponse.Decode(marshaller, buf, hdr.Version, hdr.BlockLength, rangeChecking); err != nil {
   561  			// Not much to be done here as we can't correlate
   562  			err = fmt.Errorf("failed to decode control response: %w", err)
   563  			if control.archive.Listeners.ErrorListener != nil {
   564  				control.archive.Listeners.ErrorListener(err)
   565  			}
   566  			return term.ControlledPollActionContinue
   567  		}
   568  		control.Results.ControlResponse = controlResponse
   569  		control.Results.IsPollComplete = true
   570  		control.Results.CorrelationId = controlResponse.CorrelationId
   571  		return term.ControlledPollActionBreak
   572  
   573  	case codecIds.recordingSignalEvent:
   574  		var recordingSignalEvent = new(codecs.RecordingSignalEvent)
   575  		if err := recordingSignalEvent.Decode(marshaller, buf, hdr.Version, hdr.BlockLength, rangeChecking); err != nil {
   576  			// Not much to be done here as we can't correlate
   577  			err = fmt.Errorf("failed to decode recording signal: %w", err)
   578  			if control.archive.Listeners.ErrorListener != nil {
   579  				control.archive.Listeners.ErrorListener(err)
   580  			}
   581  			return term.ControlledPollActionContinue
   582  		}
   583  
   584  	default:
   585  		logger.Debug("descriptorFragmentHandler: Insert decoder for type: %d", hdr.TemplateId)
   586  	}
   587  	return term.ControlledPollActionContinue
   588  }
   589  
   590  // PollForResponse polls for a specific correlationID
   591  // Returns (relevantId, nil) on success, (0 or relevantId, error) on failure
   592  // More complex responses are contained in Control.ControlResponse after the call
   593  func (control *Control) PollForResponse(correlationID int64, sessionID int64) (int64, error) {
   594  	logger.Debugf("PollForResponse(%d:%d)", correlationID, sessionID)
   595  
   596  	// Poll for events.
   597  	//
   598  	// As we can get async events we receive a fairly arbitrary
   599  	// number of messages here.
   600  	//
   601  	// Additionally if two clients use the same channel/stream for
   602  	// responses they will see each others messages so we just
   603  	// continually poll until we get our response or timeout
   604  	// without having completed and treat that as an error
   605  	start := time.Now()
   606  	context := PollContext{control, correlationID}
   607  
   608  	handler := aeron.NewFragmentAssembler(func(buf *atomic.Buffer, offset int32, length int32, header *logbuffer.Header) {
   609  		controlFragmentHandler(&context, buf, offset, length, header)
   610  	}, aeron.DefaultFragmentAssemblyBufferLength)
   611  	for {
   612  		ret := control.poll(handler.OnFragment, 10)
   613  
   614  		// Check result
   615  		if control.Results.IsPollComplete {
   616  			logger.Debugf("PollForResponse(%d:%d) complete, result is %d", correlationID, sessionID, control.Results.ControlResponse.Code)
   617  			if control.Results.ControlResponse.Code != codecs.ControlResponseCode.OK {
   618  				err := fmt.Errorf("Control Response failure: %s", control.Results.ControlResponse.ErrorMessage)
   619  				logger.Debug(err) // log it in debug mode as an aid to diagnosis
   620  				return control.Results.ControlResponse.RelevantId, err
   621  			}
   622  			// logger.Debugf("PollForResponse(%d:%d) success", correlationID, sessionID)
   623  			return control.Results.ControlResponse.RelevantId, nil
   624  		}
   625  
   626  		if control.Subscription.IsClosed() {
   627  			return 0, fmt.Errorf("response channel from archive is not connected")
   628  		}
   629  
   630  		if time.Since(start) > control.archive.Options.Timeout {
   631  			return 0, fmt.Errorf("timeout waiting for correlationID %d", correlationID)
   632  		}
   633  
   634  		// Idle as configured if there was nothing there
   635  		// logger.Debugf("PollForResponse(%d:%d) idle", correlationID, sessionID)
   636  		if ret == 0 {
   637  			control.archive.Options.IdleStrategy.Idle(0)
   638  		}
   639  	}
   640  
   641  	return 0, fmt.Errorf("PollForResponse out of loop") // can't happen
   642  }
   643  
   644  // DescriptorFragmentHandler is used to poll for descriptors (both recording and subscription)
   645  // The current subscription handler doesn't provide a mechanism for passing a context
   646  // so we return data via the control's Results
   647  func DescriptorFragmentHandler(context interface{}, buffer *atomic.Buffer, offset int32, length int32, header *logbuffer.Header) {
   648  	pollContext, ok := context.(*PollContext)
   649  	if !ok {
   650  		logger.Errorf("context conversion failed")
   651  		return
   652  	}
   653  
   654  	// logger.Debugf("DescriptorFragmentHandler: correlationID:%d offset:%d length: %d header: %#v\n", pollContext.correlationID, offset, length, header)
   655  
   656  	var hdr codecs.SbeGoMessageHeader
   657  
   658  	buf := new(bytes.Buffer)
   659  	buffer.WriteBytes(buf, offset, length)
   660  
   661  	marshaller := codecs.NewSbeGoMarshaller()
   662  	if err := hdr.Decode(marshaller, buf); err != nil {
   663  		// Not much to be done here as we can't correlate
   664  		err2 := fmt.Errorf("DescriptorFragmentHandler() failed to decode control message header: %w", err)
   665  		// Call the global error handler, ugly but it's all we've got
   666  		if pollContext.control.archive.Listeners.ErrorListener != nil {
   667  			pollContext.control.archive.Listeners.ErrorListener(err2)
   668  		}
   669  		return
   670  	}
   671  
   672  	// Look up our control
   673  	c, ok := correlations.Load(pollContext.correlationID)
   674  	if !ok {
   675  		// something has gone horribly wrong and we can't correlate
   676  		if pollContext.control.archive.Listeners.ErrorListener != nil {
   677  			pollContext.control.archive.Listeners.ErrorListener(fmt.Errorf("failed to locate control via correlationID %d", pollContext.correlationID))
   678  		}
   679  		logger.Debugf("failed to locate control via correlationID %d", pollContext.correlationID)
   680  		return
   681  	}
   682  	control := c.(*Control)
   683  
   684  	switch hdr.TemplateId {
   685  	case codecIds.recordingDescriptor:
   686  		var recordingDescriptor = new(codecs.RecordingDescriptor)
   687  		logger.Debugf("Received RecordingDescriptor: length %d", buf.Len())
   688  		if err := recordingDescriptor.Decode(marshaller, buf, hdr.Version, hdr.BlockLength, rangeChecking); err != nil {
   689  			// Not much to be done here as we can't correlate
   690  			err2 := fmt.Errorf("failed to decode RecordingDescriptor: %w", err)
   691  			if pollContext.control.archive.Listeners.ErrorListener != nil {
   692  				pollContext.control.archive.Listeners.ErrorListener(err2)
   693  			}
   694  			return
   695  		}
   696  		logger.Debugf("RecordingDescriptor: %#v", recordingDescriptor)
   697  
   698  		// Check this was for us
   699  		if recordingDescriptor.ControlSessionId == control.archive.SessionID && recordingDescriptor.CorrelationId == pollContext.correlationID {
   700  			// Set our state to let the caller of Poll() which triggered this know they have something
   701  			control.Results.RecordingDescriptors = append(control.Results.RecordingDescriptors, recordingDescriptor)
   702  			control.Results.FragmentsReceived++
   703  		} else {
   704  			logger.Debugf("descriptorFragmentHandler/recordingDescriptor ignoring sessionID:%d, pollContext.correlationID:%d", recordingDescriptor.ControlSessionId, recordingDescriptor.CorrelationId)
   705  		}
   706  
   707  	case codecIds.recordingSubscriptionDescriptor:
   708  		logger.Debugf("Received RecordingSubscriptionDescriptor: length %d", buf.Len())
   709  		var recordingSubscriptionDescriptor = new(codecs.RecordingSubscriptionDescriptor)
   710  		if err := recordingSubscriptionDescriptor.Decode(marshaller, buf, hdr.Version, hdr.BlockLength, rangeChecking); err != nil {
   711  			// Not much to be done here as we can't correlate
   712  			err2 := fmt.Errorf("failed to decode RecordingSubscriptioDescriptor: %w", err)
   713  			if pollContext.control.archive.Listeners.ErrorListener != nil {
   714  				pollContext.control.archive.Listeners.ErrorListener(err2)
   715  			}
   716  			return
   717  		}
   718  
   719  		// Check this was for us
   720  		if recordingSubscriptionDescriptor.ControlSessionId == control.archive.SessionID && recordingSubscriptionDescriptor.CorrelationId == pollContext.correlationID {
   721  			// Set our state to let the caller of Poll() which triggered this know they have something
   722  			control.Results.RecordingSubscriptionDescriptors = append(control.Results.RecordingSubscriptionDescriptors, recordingSubscriptionDescriptor)
   723  			control.Results.FragmentsReceived++
   724  		} else {
   725  			logger.Debugf("descriptorFragmentHandler/recordingSubscriptionDescriptor ignoring sessionID:%d, correlationID:%d", recordingSubscriptionDescriptor.ControlSessionId, recordingSubscriptionDescriptor.CorrelationId)
   726  		}
   727  
   728  	case codecIds.controlResponse:
   729  		var controlResponse = new(codecs.ControlResponse)
   730  		logger.Debugf("Received controlResponse: length %d", buf.Len())
   731  		if err := controlResponse.Decode(marshaller, buf, hdr.Version, hdr.BlockLength, rangeChecking); err != nil {
   732  			// Not much to be done here as we can't correlate
   733  			err2 := fmt.Errorf("failed to decode control response: %w", err)
   734  			if pollContext.control.archive.Listeners.ErrorListener != nil {
   735  				pollContext.control.archive.Listeners.ErrorListener(err2)
   736  			}
   737  			return
   738  		}
   739  
   740  		// Check this was for us
   741  		if controlResponse.ControlSessionId == control.archive.SessionID {
   742  			// RECORDING_UNKNOWN expected if there are no more results
   743  			if controlResponse.CorrelationId == pollContext.correlationID && controlResponse.Code == codecs.ControlResponseCode.RECORDING_UNKNOWN {
   744  				// Set our state to let the caller of Poll() which triggered this know they have something
   745  				// We're basically finished so prepare our OOB return values and log some info if we can
   746  				logger.Debugf("descriptorFragmentHandler/controlResponse: received for sessionID:%d, correlationID:%d", controlResponse.ControlSessionId, controlResponse.CorrelationId)
   747  				control.Results.ControlResponse = controlResponse
   748  				control.Results.IsPollComplete = true
   749  				return
   750  			} else if controlResponse.Code == codecs.ControlResponseCode.ERROR {
   751  				// Unexpected so log but we deal with it in the parent
   752  				logger.Debugf("ControlResponse error ERROR: %s\n%#v", controlResponse.ErrorMessage, controlResponse)
   753  				control.Results.ControlResponse = controlResponse
   754  				control.Results.IsPollComplete = true
   755  				return
   756  			} else {
   757  				logger.Debugf("descriptorFragmentHandler/controlResponse ignoring sessionID:%d, correlationID:%d", controlResponse.ControlSessionId, controlResponse.CorrelationId)
   758  			}
   759  		}
   760  
   761  	case codecIds.recordingSignalEvent:
   762  		var recordingSignalEvent = new(codecs.RecordingSignalEvent)
   763  		if err := recordingSignalEvent.Decode(marshaller, buf, hdr.Version, hdr.BlockLength, rangeChecking); err != nil {
   764  			// Not much to be done here as we can't correlate
   765  			err2 := fmt.Errorf("failed to decode recording signal: %w", err)
   766  			if pollContext.control.archive.Listeners.ErrorListener != nil {
   767  				pollContext.control.archive.Listeners.ErrorListener(err2)
   768  			}
   769  			return
   770  
   771  		}
   772  		if pollContext.control.archive.Listeners.RecordingSignalListener != nil {
   773  			pollContext.control.archive.Listeners.RecordingSignalListener(recordingSignalEvent)
   774  		}
   775  
   776  	default:
   777  		logger.Debug("descriptorFragmentHandler: Insert decoder for type: %d", hdr.TemplateId)
   778  	}
   779  }
   780  
   781  // PollForDescriptors to poll for recording descriptors, adding them to the set in the control
   782  func (control *Control) PollForDescriptors(correlationID int64, sessionID int64, fragmentsWanted int32) error {
   783  
   784  	// Update our globals in case they've changed so we use the current state in our callback
   785  	rangeChecking = control.archive.Options.RangeChecking
   786  
   787  	control.Results.ControlResponse = nil                  // Clear old results
   788  	control.Results.IsPollComplete = false                 // Clear completion flag
   789  	control.Results.RecordingDescriptors = nil             // Clear previous results
   790  	control.Results.RecordingSubscriptionDescriptors = nil // Clear previous results
   791  	control.Results.FragmentsReceived = 0                  // Reset our loop results count
   792  
   793  	start := time.Now()
   794  	descriptorCount := 0
   795  	pollContext := PollContext{control, correlationID}
   796  
   797  	for !control.Results.IsPollComplete {
   798  		logger.Debugf("PollForDescriptors(%d:%d, %d)", correlationID, sessionID, int(fragmentsWanted)-descriptorCount)
   799  		fragments := control.poll(
   800  			func(buf *atomic.Buffer, offset int32, length int32, header *logbuffer.Header) {
   801  				DescriptorFragmentHandler(&pollContext, buf, offset, length, header)
   802  			}, int(fragmentsWanted)-descriptorCount)
   803  		logger.Debugf("Poll(%d:%d) returned %d fragments", correlationID, sessionID, fragments)
   804  		descriptorCount = len(control.Results.RecordingDescriptors) + len(control.Results.RecordingSubscriptionDescriptors)
   805  
   806  		// A control response may have told us we're complete or we may have all we asked for
   807  		if control.Results.IsPollComplete || descriptorCount >= int(fragmentsWanted) {
   808  
   809  			logger.Debugf("PollNextDescriptor(%d:%d) complete", correlationID, sessionID)
   810  			return nil
   811  		}
   812  
   813  		// Check wer're live
   814  		if control.Subscription.IsClosed() {
   815  			return fmt.Errorf("response channel from archive is not connected")
   816  		}
   817  
   818  		// Check timeout
   819  		if time.Since(start) > control.archive.Options.Timeout {
   820  			return fmt.Errorf("PollNextDescriptor timeout waiting for correlationID %d", correlationID)
   821  		}
   822  
   823  		// If we received something then loop straight away
   824  		if fragments > 0 {
   825  			logger.Debugf("PollForDescriptors(%d:%d) looping with %d of %d", correlationID, sessionID, control.Results.FragmentsReceived, fragmentsWanted)
   826  			continue
   827  		}
   828  
   829  		// If we are yet to receive anything then idle
   830  		if descriptorCount == 0 {
   831  			logger.Debugf("PollForDescriptors(%d:%d) idling with %d of %d", correlationID, sessionID, control.Results.FragmentsReceived, fragmentsWanted)
   832  			control.archive.Options.IdleStrategy.Idle(0)
   833  		}
   834  
   835  	}
   836  	return nil
   837  }