Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat(plugins.d): add FUNCTION_DEL protocol command#21685

Open
ktsaou wants to merge 5 commits intonetdata:masterfrom
ktsaou:feature/function-del-protocol
Open

feat(plugins.d): add FUNCTION_DEL protocol command#21685
ktsaou wants to merge 5 commits intonetdata:masterfrom
ktsaou:feature/function-del-protocol

Conversation

@ktsaou
Copy link
Member

@ktsaou ktsaou commented Jan 30, 2026

Summary

  • Add FUNCTION_DEL protocol command to allow plugins to dynamically unregister functions without restarting
  • Enables go.d.plugin to properly cleanup functions when collectors are disabled via DYNCFG
  • Functions become unavailable via flag (not removed from dictionary) allowing unlimited register/unregister cycles

Changes

Core:

  • Add unregistered flag to rrd_host_function struct
  • Add centralized rrd_function_is_available() helper used by all availability checks
  • Update all 6 export functions to filter unavailable functions

Protocol:

  • Add FUNCTION_DEL [GLOBAL] "name" command (keyword ID 44)
  • Add STREAM_CAP_FUNCTION_DEL capability (bit 27, name "FUNCDEL")
  • Stream deletions directly to parent for multi-hop support

Safety:

  • Ownership validation: only the registering collector can unregister
  • internal parameter for trusted callers (dyncfg, health)
  • Graceful degradation for parents without capability

Protocol Format

FUNCTION_DEL [GLOBAL] "function_name"

API Behavior

  • Unregistered functions return HTTP 503: "This function has been unregistered by the plugin"
  • Functions disappear from /api/v1/functions listing
  • Re-registration with FUNCTION command restores availability

Test plan

  • Basic registration/unregistration cycle
  • Ownership validation (plugin A cannot unregister plugin B's function)
  • Concurrent access (function call during unregistration)
  • Multiple register/unregister cycles (100+ times)
  • Streaming with capability negotiation
  • Streaming without capability (graceful degradation)
  • Multi-hop streaming topology
  • API/JSON export filtering
  • Plugin restart behavior unchanged

Summary by cubic

Adds the FUNCTION_DEL protocol command so plugins can unregister functions at runtime. This lets go.d.plugin clean up functions when collectors are disabled via DYNCFG, without restarting.

  • New Features
    • New command: FUNCTION_DEL [GLOBAL] "name" marks a function unavailable without removing it.
    • Core: added unregistered flag and rrd_function_is_available(); exporters and listings skip unavailable functions; API returns 503 for unregistered calls; re-registering with FUNCTION restores availability.
    • Streaming: added STREAM_CAP_FUNCTION_DEL (FUNCDEL); deletions are sent upstream and work across multi-hop; older parents without the capability degrade gracefully.
    • Safety: only the owning collector can unregister; internal flag for trusted callers (dyncfg, health).
    • Go API: FUNCTIONREMOVE now sends FUNCTION_DEL GLOBAL.

Written for commit ca6ca7d. Summary will update on new commits.

…on unregistration

Add the FUNCTION_DEL protocol command to allow plugins (especially go.d.plugin)
to dynamically unregister functions without restarting. This enables proper
cleanup when collectors are disabled via DYNCFG.

Changes:
- Add `unregistered` flag to rrd_host_function struct
- Add centralized `rrd_function_is_available()` helper for all availability checks
- Update all 6 export functions to use the availability helper
- Add `rrd_function_del()` with ownership validation and streaming support
- Add STREAM_CAP_FUNCTION_DEL capability for parent/child negotiation
- Add parser handler for FUNCTION_DEL keyword (ID 44, worker job +41)
- Stream deletions directly to parent (not via flag mechanism)
- Support multi-hop streaming topology
- Add `internal` parameter for trusted internal callers (dyncfg, health)

Protocol format:
  FUNCTION_DEL [GLOBAL] "function_name"

Behavior:
- Functions remain in dictionary but become unavailable via flag
- Re-registration clears the flag (unlimited register/unregister cycles)
- API returns 503 with "This function has been unregistered by the plugin"
- Graceful degradation: old parents without capability don't receive deletions
@ktsaou
Copy link
Member Author

ktsaou commented Jan 30, 2026

@ilyam8 this needs testing. It touches a lot of things...

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 17 files

Confidence score: 5/5

  • Minor documentation cleanup needed: src/plugins.d/README.md has a duplicate FUNCTION_DEL entry in the command list, which could cause small confusion for readers.
  • Low severity (3/10) and limited to README content, so merge risk is minimal.
  • Pay close attention to src/plugins.d/README.md - remove the duplicate command entry.
Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/plugins.d/README.md">

<violation number="1" location="src/plugins.d/README.md:129">
P3: Remove the duplicate `FUNCTION_DEL` entry from the command list so each command appears only once.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant P as External Plugin (go.d.plugin)
    participant C as Netdata Core (Parser/Registry)
    participant DB as RRDHOST Functions (Dictionary)
    participant S as Stream Sender (Streaming)
    participant Parent as Parent Netdata
    participant API as API v1 (Functions)

    Note over P, Parent: Registration & Unregistration Flow

    P->>C: FUNCTION "my_func" ...
    C->>DB: rrd_function_add()
    DB-->>DB: CHANGED: Reset 'unregistered' flag if exists

    rect rgb(23, 37, 84)
    Note right of P: Dynamic Cleanup (e.g., via DYNCFG)
    P->>C: NEW: FUNCTION_DEL [GLOBAL] "my_func"
    C->>DB: NEW: rrd_function_del(name, internal_flag)
    
    alt Ownership Validation
        DB->>DB: NEW: Check if current collector == registered collector
    else internal_flag == true
        DB->>DB: Bypass ownership check (trusted caller)
    end

    alt Authorized
        DB->>DB: NEW: Set 'unregistered' flag = true
        C->>S: NEW: stream_send_function_del()
        opt Parent Cap: FUNCDEL
            S->>Parent: NEW: FUNCTION_DEL GLOBAL "my_func"
        end
    else Unauthorized (Collector Mismatch)
        DB-->>C: Return Error (Log Warning)
    end
    end

    Note over API, DB: Runtime Access Flow

    API->>DB: rrd_function_is_available()
    
    alt unregistered == true
        DB-->>API: return false
        API-->>API: NEW: Return HTTP 503 (Service Unavailable)
    else unregistered == false AND collector_running == true
        DB-->>API: return true
        API->>P: Execute Function
    end

    Note over API, DB: Discovery Flow
    API->>DB: GET /api/v1/functions
    DB->>DB: CHANGED: rrd_function_is_available() check during iteration
    Note right of DB: NEW: Exporters/JSON filters out 'unregistered' entries
    DB-->>API: Return JSON (excluding deleted functions)
Loading

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@ktsaou
Copy link
Member Author

ktsaou commented Jan 30, 2026

@ilyam8 btw, I think the go.d.plugin registers functions per module, not per job. This means that this PR is not actually needed, since functions are always registered independently of the jobs.

This PR however enables go.d.plugin to improve its framework and register/unregister job-level functions, which will allow each function to have its own required parameters (nothing shared between jobs).

@ktsaou ktsaou requested a review from Copilot January 30, 2026 22:37
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new FUNCTION_DEL plugins.d/streaming protocol command so plugins can dynamically unregister previously registered functions (without restarting), by marking functions as unavailable and filtering them from exports/listings.

Changes:

  • Core: add an unregistered flag to function records, introduce rrd_function_is_available(), and filter unavailable functions across exporters/lookups.
  • Protocol: add FUNCTION_DEL keyword (ID 44) plus STREAM_CAP_FUNCTION_DEL (“FUNCDEL”) and a sender helper to stream deletions upstream.
  • Plugins.d: extend parser/keyword tables and documentation to recognize and describe FUNCTION_DEL.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/streaming/stream-capabilities.h Adds STREAM_CAP_FUNCTION_DEL capability bit.
src/streaming/stream-capabilities.c Registers “FUNCDEL” capability name and advertises it in our capabilities.
src/streaming/protocol/commands.h Declares stream_send_function_del() API for streaming deletions.
src/streaming/protocol/command-function-del.c Implements sender-side emission of FUNCTION_DEL GLOBAL ... when capability is present.
src/plugins.d/pluginsd_parser.c Routes FUNCTION_DEL keyword to the new handler.
src/plugins.d/pluginsd_functions.h Declares pluginsd_function_del().
src/plugins.d/pluginsd_functions.c Parses FUNCTION_DEL [GLOBAL] name and calls rrd_function_del(...).
src/plugins.d/gperf-hashtable.h Regenerates keyword hash table and assigns ID 44 to FUNCTION_DEL.
src/plugins.d/gperf-config.txt Adds FUNCTION_DEL keyword definition and mapping for gperf generation.
src/plugins.d/README.md Documents the new FUNCTION_DEL command and its behavior.
src/libnetdata/functions_evloop/functions_evloop.h Adds PLUGINSD_KEYWORD_FUNCTION_DEL macro.
src/database/rrdfunctions.h Changes rrd_function_del() signature and exposes rrd_function_is_available().
src/database/rrdfunctions.c Implements “unregister” behavior, availability helper, ownership checks, and 503 messaging.
src/database/rrdfunctions-internals.h Extends rrd_host_function with unregistered and declares availability helper.
src/database/rrdfunctions-exporters.c Filters unavailable/unregistered functions from streaming/JSON/dict exporters.
src/daemon/dyncfg/dyncfg.c Updates call site for new rrd_function_del() signature (internal delete).
CMakeLists.txt Adds new command-function-del.c to the build.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +55 to +58
if(__atomic_load_n(&rdcf->unregistered, __ATOMIC_RELAXED)) {
__atomic_store_n(&rdcf->unregistered, false, __ATOMIC_RELAXED);
changed = true;
}
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rrd_function_add() computes rrd_host_function.options, but rrd_functions_conflict_callback() never reconciles/updates rdcf->options when a function is re-registered. With the new FUNCTION_DEL behavior (entries stay in host->functions and re-registering will hit the conflict callback), a re-register that changes scope (GLOBAL vs LOCAL) or restricted/dyncfg flags will keep stale options and can break filtering/streaming. Consider comparing and updating rdcf->options from new_rdcf->options (like the other fields).

Copilot uses AI. Check for mistakes.
Comment on lines +292 to 296
if(!st)
stream_send_function_del(host, key);

if(!st)
rrdhost_flag_set(host, RRDHOST_FLAG_GLOBAL_FUNCTIONS_UPDATED);
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rrd_function_del() only streams FUNCTION_DEL upstream for global functions (when st == NULL). Chart-scoped functions are streamed to parents during CHART definition, so unregistering a chart function on a multi-hop topology will leave stale functions on the parent(s). If FUNCTION_DEL is meant to support multi-hop generally, consider streaming deletions for chart functions too (e.g., send a GLOBAL FUNCTION_DEL by name, or otherwise ensure the parent can reliably apply the deletion).

Copilot uses AI. Check for mistakes.
@ilyam8
Copy link
Member

ilyam8 commented Jan 31, 2026

I think the go.d.plugin registers functions per module, not per job

@ktsaou I've expanded the go.d.plugin's function framework.

  • Now it can do both - per module and per module per job.
  • go.d/sql registers functions (plural) for each data collection job. Functions are SQL queries defined by users.

@ilyam8 ilyam8 self-requested a review as a code owner February 5, 2026 18:07
@github-actions github-actions bot added area/collectors Everything related to data collection collectors/go.d area/go labels Feb 5, 2026
@ilyam8 ilyam8 requested a review from Copilot February 5, 2026 18:17
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

struct rrd_host_function *t;
dfe_start_read(rrdset_functions_view, t) {
if(!rrd_collector_running(t->collector)) continue;
if(!rrd_function_is_available(t, NULL)) continue;
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rrd_function_is_available call passes NULL for the host parameter, which bypasses the rrdhost_state_id check. This is inconsistent with similar functions like stream_sender_send_rrdset_functions (line 12) and chart_functions2json (which calls functions2json with st->rrdhost). Since rrdinstance_acquired_rrdhost() is available to get the host from the instance acquired object in the caller (src/web/api/formatters/jsonwrap.c:57), the host pointer should be obtained and passed here to ensure proper state validation, especially after this PR introduces function unregistration.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants