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 }