Conversation
Send an NFUpdate PATCH with nfStatus REGISTERED at the interval the NRF returns, per 3GPP TS 29.510 clause 5.2.2.3.2. Re-adopt heartBeatTimer from every answer. Re-register on 404 or after three consecutive failures. The webconsole registers as an AF, and the NRF sweep that suspends silent instances filters on nfStatus and lastHeartBeat alone, so the AF profile goes stale like any other without this. The heartbeat loop lives in the util nfheartbeat package; the webui context only supplies the PATCH and re-registration transport. The nfHeartBeatTimer config option only sets the fallback interval. The NRF value always wins. SendNFRegistration takes a context and an applyOAuth2 flag. The retry wait is interruptible, so a shutdown during an NRF outage no longer sleeps through it, and OAuth2Required is written only by the startup registration: the request handlers read it concurrently, so the re-registration from the heartbeat goroutine must not race them. Also drop the instance ID parsed from the register response Location: the generated client only fills it on 201, so a re-registration against an NRF that still holds the profile was overwriting NfInstanceID with an empty string. That branch also never broke out of the retry loop, so a successful registration always sent a second PUT. Init returns an error now, since it builds the heartbeat runner, and the app carries a context that Terminate cancels before waiting for the heartbeat goroutine and deregistering.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hello,
This PR is part of a series of PRs that will also modify the other SBI NFs and the configuration file. It depends on the util shared
nfheartbeatpackage, and on the NRF PR enforcing the procedure on the other side.free5gc/nrf#95
It addresses the following:
context.Background()and sleeps 2s between attempts without watching any context, so a shutdown during an NRF outage waits it out, and a heartbeat or re-registration could land on the NRF after the NFDeregisterPresentation of the changes
heartbeat feature
After a successful registration, the webconsole starts the heartbeat runner from
util/nfheartbeat, which sends the NFUpdate PATCH of clause 5.2.2.3.2 (replace/nfStatusREGISTERED) at the current interval. The loop, the interval ownership, the recovery and the panic containment live in the shared package; the webconsole supplies only the transport, through a smallnrfRegistraradapter. The NRF calls ofwebui_contextare package level rather than methods on a consumer service, so the adapter carries no state of its own:UpdateNFInstanceis the newSendUpdateNFInstance.RegisterNFInstanceis the existingSendNFRegistration, and returns the heartBeatTimer the NRF assigned.Behaviour that follows from the shared runner: the NRF owns the interval (adopted from the registration response, re-armed by every 200 answer carrying a new value, a 204 leaves it as is), a 404 on the heartbeat re-registers immediately, 3 consecutive failures of any kind do too.
A new optional
nfHeartBeatTimerconfiguration IE serves as fallback while the NRF has not assigned an interval.The heartbeat starts from the registration goroutine in
Start, once the registration has succeeded andIsRegisteredis set: a profile that was never registered has nothing to keep alive.Runner.Startis a no-op once its context is done, so that goroutine racing a shutdown is safe.On shutdown the ordering matters:
Terminatecancels the application context, which is what stops the loop, then waits for the heartbeat goroutine to exit before sending the NFDeregister, inside the existingIsRegisteredguard. Nothing else cancels that context, so the cancel has to come first or the wait would never return.SendUpdateNFInstance
New call for the NFUpdate PATCH. It returns the raw error alongside any ProblemDetails, so the runner can read the
GenericOpenAPIErrorstatus and classify a 404 even when the NRF answers without a problem body.It honours the caller's context.
GetTokenCtxtakes no parent, so the token request itself stays uncancelable; transplanting the token into the caller's context lets at least the PATCH observe a shutdown.Registration hardening
SendNFRegistrationtakes a context instead of usingcontext.Background(), and the 2s retry wait is interruptible, so a shutdown during an NRF outage returns immediately instead of sleeping through it.RetrySendNFRegistrationforwards it. TheMaxRetryAttemptsbound is kept: the webconsole deliberately gives up and runs with limited functionality rather than retrying forever.processRegisterResponse, which adopts the heartBeatTimer along with the oauth2 custom info.applyOAuth2argument.OAuth2Requiredis read concurrently by the request handlers once the web server is running, so a re-registration from the heartbeat goroutine may not write it: if the NRF flips the setting later, the webconsole logs a warning asking for a restart instead of racing the handlers.NfInstanceIDis no longer written from it. NFRegister is a PUT on the instance ID the webconsole chose (clause 6.1.3.2.2), so the NRF echoes that ID back and the parsing could only reproduce it or corrupt it. The generated client fillsLocationonly on a 201, so every re-registration against an NRF that still held the profile answered 200 and set the instance ID to the empty string. That same branch also never broke out of the retry loop, so even a successful first registration always sent a second PUT before the 200 path ended it.Other changes
webui_context.Initreturns an error now, since it builds the heartbeat runner;NewAppforwards it.WebuiAppcarries thecontext.Contextandcontext.CancelFuncthe heartbeat runs on.Terminatecancels it.There is no nil-client guard to add here: the webconsole builds its single
NFManagementClientonce inInit, so it is never nil.New configuration IE (optional):
Testing
Neither
webui_contextnorbackend/factoryhad tests. This PR adds the harness along with the heartbeat coverage. The NRF calls read package globals, so the harness owns those globals for the duration of a test and restores them afterwards; no mocking framework is introduced.SendUpdateNFInstanceis tested for the 200 with profile, the 204, the ProblemDetails extraction and the missing nrfUri;SendNFRegistrationfor the 201 and 200 handling, the retry until success, the return on context cancellation, and the exhaustedMaxRetryAttempts;SendDeregisterNFInstancefor the 204 and the 404 with its ProblemDetails.webui_contexttests read unexported state, so they are in-package and named*_internal_test.go, which is what the repositorytestpackagelinter expects; the factory test is external, inpackage factory_test.testing/synctestfake clock rather than real sleeps.{ _id: ObjectId('6ab15cd924a2cfb3f3677ed8'), heartBeatTimer: 10, plmnList: [ { mcc: '208', mnc: '93' } ], customInfo: { AfType: 'webconsole', oauth2: false }, nfInstanceId: 'aeb27f21-2d6a-45a9-9824-a734e3ec5e6d', nfType: 'AF', nfStatus: 'REGISTERED', lastHeartBeat: '2026-09-21T16:35:47Z' }This work is sponsored by Free Mobile!