Skip to content

feat(consumer): send periodic NF heartbeat to NRF - #67

Open
Niahh wants to merge 1 commit into
free5gc:mainfrom
Niahh:feat/nrf-heartbeat
Open

feat(consumer): send periodic NF heartbeat to NRF#67
Niahh wants to merge 1 commit into
free5gc:mainfrom
Niahh:feat/nrf-heartbeat

Conversation

@Niahh

@Niahh Niahh commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

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 PR adding the shared nfheartbeat package, and on the NRF PR enforcing the procedure on the other side.

free5gc/util#48
free5gc/nrf#95

It addresses the following:

  • The AUSF registers once to the NRF and never heart-beats, so with the new NRF-side enforcement (TS 29.510 clause 5.2.2.3) its profile would be SUSPENDED then dropped as stale
  • The heartBeatTimer assigned by the NRF in the registration response is ignored
  • The NFRegister retry loop is not interruptible and runs on context.Background(), so a shutdown during a retry hangs, and a heart-beat or re-registration could land on the NRF after the NFDeregister

Presentation of the changes

Heart-beat feature

After a successful registration, the AUSF starts the heart-beat runner from util/nfheartbeat, which sends the NFUpdate PATCH of clause 5.2.2.3.2 (replace /nfStatus REGISTERED) at the current interval. The loop, the interval ownership, the recovery and the panic containment live in the shared package; the AUSF supplies only the transport, through a small nrfRegistrar adapter over nnrfService:

  • UpdateNFInstance is the new SendUpdateNFInstance.
  • RegisterNFInstance is the existing registration, 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 heart-beat re-registers immediately, 3 consecutive failures of any kind do too, and a panic inside a tick costs one heart-beat instead of ending them all.

A new optional nfHeartBeatTimer configuration IE serves as fallback while the NRF has not assigned an interval.

On shutdown the ordering matters: terminateProcedure waits for the heart-beat goroutine to exit before sending the NFDeregister, so no heart-beat PATCH or re-registration PUT can resurrect the profile on the NRF afterwards.

SendUpdateNFInstance

New consumer call for the NFUpdate PATCH. It returns the raw error alongside any ProblemDetails, so the runner can read the GenericOpenAPIError status and classify a 404 even when the NRF answers without a problem body.

It honours the caller's context. GetTokenCtx takes no parent, so the token request itself stays uncancellable; transplanting the token into the caller's context lets at least the PATCH observe a shutdown.

Registration hardening

  • RegisterNFInstance now runs on the application context instead of context.Background(), and the 2s retry wait is interruptible, so a shutdown during an NRF outage returns immediately instead of sleeping through it.
  • The response handling was extracted into processRegisterResponse and now also covers the 200 (profile already known to the NRF), which previously fell through without adopting anything, heartBeatTimer included.
  • The OAuth2 custom info is only applied to the AUSF context on the startup registration, hence the new applyOAuth2 argument. OAuth2Required is read concurrently by the SBI handlers once the server runs, so a re-registration from the heart-beat goroutine may not write it: if the NRF flips the setting later, the AUSF logs a warning asking for a restart instead of racing the handlers.
  • RegisterNFInstance returns only an error now. It no longer parses the Location header: NFRegister is a PUT on the instance ID the AUSF chose (clause 6.1.3.2.2), so the NRF echoes that ID back and the parsing could only reproduce it or corrupt it. A Location ending in a slash yielded an empty ID, which was then written to the AUSF context.

Other changes

These are pre-existing, but the heart-beat goroutine is what makes them reachable.

  • getNFManagementClient returns nil when nrfUri is empty. RegisterNFInstance, SendUpdateNFInstance and SendDeregisterNFInstance all dereferenced it. They now report the error the way SendSearchNFInstances already did, so a misconfigured nrfUri is an error instead of a panic on every heart-beat tick.
  • Both NRF client caches released the read lock, took the write lock and then overwrote the map entry unconditionally. The heart-beat goroutine is the first caller to reach the NFManagement path concurrently with the SBI handlers, so the caches now re-check under the write lock.
  • SendDeregisterNFInstance took its token context from the global ausf_context.GetSelf() while taking its client from the injected context. Both resolve to the same pointer at runtime; it now uses the injected one consistently.

New configuration IE:

nfHeartBeatTimer: 10  # fallback heart-beat interval in seconds (1~3600), used only
                      # until the NRF assigns one; the NRF value always takes precedence

The field is optional and defaults to nfheartbeat.DefaultTimer, 10 seconds, which matches the NRF default interval. A longer fallback would reach past the deadline at which the NRF suspends a silent instance. The 1~3600 range mirrors the heartBeatTimer bounds of the NRF profile validation.

Testing

  • Unit tests cover the wiring rather than the runner internals, which the util PR covers: registration seeding the timer, the loop sending the PATCH through the real consumer transport, the 404 handshake re-registering with a PUT and re-arming on the interval the re-registration returned, and the fallback interval coming from the config.
  • SendUpdateNFInstance is tested for the 200 with profile, the 204, the ProblemDetails extraction and the missing nrfUri; RegisterNFInstance for the 201 and 200 handling, the retry until success, and the return on context cancellation.
  • Config tests cover the default and the range validation.
  • The NRF is mocked with gock, so the suite needs no network, and the timing-dependent cases run on the testing/synctest fake clock rather than real sleeps.

This work is sponsored by Free Mobile!

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 heartbeat loop lives in the util nfheartbeat package; the consumer
only supplies the PATCH and re-registration transport.

The nfHeartBeatTimer config option only sets the fallback interval. The
NRF value always wins.

Also drop the instance ID parsed from the register response Location.
Guard the NRF client lookups against a nil client. Make the
registration retry cancellable. Wait for the heartbeat goroutine
before deregistering.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant