Skip to content

fix(config-profiles): restart nodes on snippet create/update/delete#183

Open
TopPro104 wants to merge 1 commit into
remnawave:devfrom
TopPro104:fix/snippet-update-restart-nodes
Open

fix(config-profiles): restart nodes on snippet create/update/delete#183
TopPro104 wants to merge 1 commit into
remnawave:devfrom
TopPro104:fix/snippet-update-restart-nodes

Conversation

@TopPro104

Copy link
Copy Markdown

Description

Snippets are expanded into the node config at generation time via XRayConfig.replaceSnippets() (see get-prepared-config-with-users.handler.ts). However, creating, updating or deleting a snippet did not trigger any node restart — unlike updating a config profile, which calls startAllNodesByProfile.

As a result, after editing a snippet the panel saved the new value but nodes kept serving the old config until a manual node restart.

Fix

Trigger nodesQueuesService.startAllNodes() after snippet create, update and delete in SnippetsService, so the regenerated config (with the new snippet values) is propagated to nodes automatically.

Since snippets are global and may be referenced by any config profile, startAllNodes is used rather than restarting a single profile's nodes.

Closes remnawave/panel#398

@snyk-io

snyk-io Bot commented Jun 2, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@greptile-apps

greptile-apps Bot commented Jun 2, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes a bug where creating, updating, or deleting a snippet did not trigger a node restart, leaving nodes serving stale configs until manually restarted. It injects NodesQueuesService into SnippetsService and calls startAllNodes after each successful snippet mutation.

  • NodesQueuesService is now injected into SnippetsService and startAllNodes is called with a descriptive emitter string after create, update, and delete operations; the service is globally available via the @Global() QueueModule so no module registration changes are needed.
  • The added SECURITY.md is an unfilled GitHub template placeholder with no actual reporting policy.

Confidence Score: 4/5

The core fix is correct and follows the existing queue-dispatch pattern in the codebase, but the startAllNodes call sits after an already-committed DB write with no isolation, so a transient Redis failure leaves the snippet persisted while the caller receives an error.

The queue dispatch for create/update/delete is logically sound and uses proper deduplication. The risk is that if BullMQ is briefly unavailable after a snippet is written to the database, the caller gets an error and — for createSnippet — cannot retry without hitting the unique-name constraint. This is the same unguarded pattern used in ConfigProfileService, but it is most disruptive on the create path because the write is not idempotent.

src/modules/config-profiles/snippets.service.ts — specifically the error-handling boundaries around the startAllNodes calls in createSnippet and deleteSnippetByName.

Important Files Changed

Filename Overview
src/modules/config-profiles/snippets.service.ts Injects NodesQueuesService and calls startAllNodes after each snippet mutation (create/update/delete); the service is globally available via @global() QueueModule so no module registration change is needed.
SECURITY.md Adds a placeholder SECURITY.md with no actual content — the template boilerplate was not filled in.

Sequence Diagram

sequenceDiagram
    participant Client
    participant SnippetsService
    participant SnippetsRepository
    participant NodesQueuesService
    participant BullMQ

    Client->>SnippetsService: createSnippet / updateSnippet / deleteSnippetByName
    SnippetsService->>SnippetsRepository: create / update / deleteByName
    SnippetsRepository-->>SnippetsService: OK (committed to DB)
    SnippetsService->>NodesQueuesService: "startAllNodes({ emitter })"
    NodesQueuesService->>BullMQ: enqueue START_ALL_NODES (deduplicated)
    BullMQ-->>NodesQueuesService: job added
    NodesQueuesService-->>SnippetsService: OK
    SnippetsService->>SnippetsService: getSnippets()
    SnippetsService-->>Client: updated snippet list
Loading

Reviews (1): Last reviewed commit: "fix(config-profiles): restart nodes on s..." | Re-trigger Greptile

Comment thread SECURITY.md Outdated
Comment on lines +1 to +9
# Security Policy

## Reporting a Vulnerability

Use this section to tell people how to report a vulnerability.

Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Placeholder SECURITY.md with no actual policy

This file contains only the GitHub-generated template text. Before merging, it should be filled in with the actual vulnerability reporting contact, expected response times, and disclosure process. Shipping an empty template signals to security researchers that there is no real reporting channel.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +71 to +73
await this.snippetsRepository.create(snippetEntity);

await this.nodesQueuesService.startAllNodes({ emitter: 'createSnippet' });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Queue error after committed write can strand a created snippet

If startAllNodes throws (e.g. Redis unavailable), the snippetsRepository.create has already committed to the database, but the caller receives an error response. On retry the user gets SNIPPET_NAME_ALREADY_EXISTS, making the snippet unreachable through the normal create flow. The delete case is similar: the row is gone, but the caller sees an error and the follow-up retry returns SNIPPET_NOT_FOUND. This pattern is the same one used in ConfigProfileService.updateConfigProfile, so it is a codebase-level concern, but it is most disruptive here for createSnippet because of the non-idempotent unique constraint.

@TopPro104 TopPro104 force-pushed the fix/snippet-update-restart-nodes branch from bdc973f to 34bed0e Compare June 2, 2026 19:06
@kastov

kastov commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Вообще, такая логика была намеренной, так как ручной перезапуск в некоторых случаях намного лучше, чем постоянные перезапуске при каждом создании/изменении/тд сниппета.

Подумаю на счет вашей реализации.

…/delete

Snippets are expanded into the node config at generation time via
XRayConfig.replaceSnippets(), but creating, updating or deleting a
snippet did not trigger any node restart, unlike config profile
updates. As a result nodes kept serving the previous snippet values
until a manual restart.

Restart only the nodes of config profiles that actually reference the
changed snippet (mirroring how updating a config profile restarts its
own nodes via startAllNodesByProfile), instead of restarting every
node. Failures while enqueuing restarts are logged and swallowed, since
the snippet mutation itself has already been committed.

Adds XRayConfig.getReferencedSnippetNames() to resolve which profiles
use a given snippet.

Closes remnawave/panel#398
@TopPro104 TopPro104 force-pushed the fix/snippet-update-restart-nodes branch from 34bed0e to fa7a0b9 Compare June 2, 2026 19:24
@TopPro104

Copy link
Copy Markdown
Author

Согласен, рестартить все ноды это неправильно. Переделал, теперь перезапускаются только ноды профилей, которые реально ссылаются на изменённый сниппет

@TopPro104

Copy link
Copy Markdown
Author

Вообще, такая логика была намеренной, так как ручной перезапуск в некоторых случаях намного лучше, чем постоянные перезапуске при каждом создании/изменении/тд сниппета.

Кстати, если в каких-то сценариях ручной перезапуск удобнее то могу добавить настройку, чтобы авто-рестарт при изменении сниппета можно было включать/выключать. Дефолт на ваше усмотрение.

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.

2 participants