Skip to content

Fix load order of APIs #2413#2414

Merged
rrayst merged 9 commits into
masterfrom
load-order-fix-2413
Dec 10, 2025
Merged

Fix load order of APIs #2413#2414
rrayst merged 9 commits into
masterfrom
load-order-fix-2413

Conversation

@predic8
Copy link
Copy Markdown
Member

@predic8 predic8 commented Dec 8, 2025

fixes #2413

Summary by CodeRabbit

  • Refactor

    • Bean lifecycle split into two phases: register definitions, then explicitly start processing; explicit start calls added where needed.
  • Behavior

    • Activation order made deterministic (insertion-order) and processing runs as a single startup pass rather than auto-starting on registration.
  • Tests

    • Test fixtures updated to implement the new startup hook (no-op where appropriate).

✏️ Tip: You can customize this high-level summary in your review settings.

- Added `start()` method to `BeanRegistry` interface and its implementation for lifecycle management.
- Documented thread-safety expectations and constraints for `registerBeanDefinitions()` and `start()`.
- Updated usage in relevant classes to adhere to the modified lifecycle flow.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 8, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Separates bean registration from activation by adding a start() lifecycle method to BeanRegistry; registration now queues events and activation is performed by an explicit start() call. Call sites and tests updated to invoke start(); implementation preserves insertion order for activation.

Changes

Cohort / File(s) Summary
BeanRegistry interface
annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistry.java
Added public void start() to the interface; imports consolidated to java.util.*.
BeanRegistry implementation
annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistryImplementation.java
Separated registration from activation: registerBeanDefinitions(...) no longer calls start(); uidsToActivate changed to LinkedHashSet to preserve insertion order; comments/Javadoc updated.
Call sites (runtime & parsing)
core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java, annot/src/main/java/com/predic8/membrane/annot/util/YamlParser.java
Callers now explicitly call registry.start() after registering bean definitions (RouterCLI and YamlParser adjusted accordingly).
Tests
core/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.java
Test registry updated with a no-op public void start() implementation to satisfy the new interface method.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant CLI as RouterCLI
    participant Parser as YamlParser
    participant Registry as BeanRegistryImplementation
    participant Router as Router

    CLI->>Registry: new BeanRegistryImplementation(router, grammar)
    CLI->>Parser: parseMembraneResources(...)
    Parser->>Registry: registerBeanDefinitions(listOfBeanDefs)
    Parser->>Registry: (returns)
    CLI->>Registry: registry.start()
    Registry->>Registry: process queued ADDED events (in insertion order)
    Registry->>Router: activate beans / register components
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review focus:
    • Verify LinkedHashSet preserves activation order without introducing concurrency issues.
    • Ensure all registration call sites (including other parsers/tests) call start() where required.
    • Confirm event queuing + activation semantics match intended lifecycle and linked issue #2413 expectations.

Possibly related PRs

Poem

🐰 I hopped through YAML rows at dawn’s light,
I queued the beans in tidy sight.
Register first, then start the run,
Each API awakens, one by one. 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Fix load order of APIs #2413' clearly summarizes the main objective: fixing the load order issue described in issue #2413.
Linked Issues check ✅ Passed The PR implements the core requirement from #2413: preserving the sequence of API definitions from YAML by introducing an explicit lifecycle with registerBeanDefinitions and start() methods that maintain insertion order through LinkedHashSet.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing the load order issue: refactoring BeanRegistry lifecycle, updating implementation to preserve insertion order, and applying changes consistently across test files and CLI code.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 55a948b and 3524869.

📒 Files selected for processing (2)
  • annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistry.java (2 hunks)
  • annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistryImplementation.java (1 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistryImplementation.java (2)

166-172: Potential duplicate UID entries in uidsToActivate.

If a bean is first ADDED and then later MODIFIED before activationRun() executes, uidsToActivate.add(bd.getUid()) will insert the same UID twice. The removal loop at lines 201-202 uses List.remove(Object) which only removes the first occurrence, leaving duplicates behind.

Consider checking for existing entries before adding, or use a LinkedHashSet to maintain order while preventing duplicates:

-    private final List<String> uidsToActivate = new ArrayList<>();
+    private final Set<String> uidsToActivate = new LinkedHashSet<>();

Or guard the add:

         if (observer.isActivatable(bd))
-            uidsToActivate.add(bd.getUid());
+            if (!uidsToActivate.contains(bd.getUid()))
+                uidsToActivate.add(bd.getUid());

113-129: Kubernetes watch events won't be processed; start() exits when queue empties and never consumes subsequent events.

The while (!changeEvents.isEmpty()) loop exits immediately when the queue is empty, then returns. In Kubernetes mode, watchers add events asynchronously via beanRegistry.handle() after start() has already returned. These queued events are never processed because no thread is consuming them.

The javadoc explicitly states: "In Kubernetes mode, run this method in a dedicated long-running thread." However, KubernetesWatcher.start() calls beanRegistry.start() synchronously and doesn't create a background thread. The fix requires either:

  1. Removing the isEmpty() check so the loop blocks on take() indefinitely in a dedicated thread, or
  2. Having KubernetesWatcher submit beanRegistry.start() to an executor service that keeps it running.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b7235c and d6c2095.

📒 Files selected for processing (5)
  • annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistry.java (1 hunks)
  • annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistryImplementation.java (1 hunks)
  • annot/src/test/java/com/predic8/membrane/annot/util/YamlParser.java (1 hunks)
  • core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java (1 hunks)
  • core/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.java (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Automated tests
  • GitHub Check: Analyze (java)
🔇 Additional comments (4)
annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistry.java (1)

26-79: Well-documented lifecycle contract.

The Javadoc clearly explains the thread-safety model and lifecycle expectations for both methods. The separation of registration and activation phases is a sound design that enables order-preserving processing.

annot/src/test/java/com/predic8/membrane/annot/util/YamlParser.java (1)

51-55: Correctly implements the new lifecycle pattern.

The sequence registerBeanDefinitions()start()await() properly follows the documented contract. The start() method processes events synchronously, so awaiting the latch afterward is correct.

core/src/test/java/com/predic8/membrane/core/kubernetes/GenericYamlParserTest.java (1)

348-351: Appropriate no-op implementation for test stub.

The empty start() method correctly satisfies the interface contract for this test registry, which only serves as a stub for bean resolution.

annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistryImplementation.java (1)

44-44: Good: ArrayList preserves insertion order, fixing the load order issue.

Switching from Set<String> to List<String> correctly preserves the registration order of APIs, which is the core fix for issue #2413.

Comment thread core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java Outdated
predic8 and others added 2 commits December 8, 2025 22:32
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistryImplementation.java (2)

38-45: Deterministic activation order via uidsToActivate looks good; consider guarding with tests

Using a LinkedHashSet for uidsToActivate while keeping bds as a concurrent map cleanly decouples storage from activation order and should fix the scrambled startup order, assuming producers enqueue in YAML order.

I’d recommend adding/adjusting a focused test that registers multiple API BeanDefinitions in a known sequence via registerBeanDefinitions(...) and asserts that the observer sees activation in exactly that sequence, to lock in this behavior.


51-80: Lifecycle split (registerBeanDefinitions vs start) is clearer; verify all callers now invoke start()

The new Javadoc and behavior (register only, no implicit startup) make the registry lifecycle much clearer and avoid hidden side effects. One concern: any existing call sites that previously relied on registerBeanDefinitions(...) implicitly triggering startup will now silently skip activation if they don’t call start().

Two suggestions:

  • Double‑check all implementors/callers of BeanRegistry (beyond YamlParser and RouterCLI) to ensure they now call start() once after registration.
  • Optionally rename the parameter bds to something like definitions to avoid shadowing/confusion with the bds field.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d6c2095 and 0046e1f.

📒 Files selected for processing (2)
  • annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistryImplementation.java (1 hunks)
  • core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/src/main/java/com/predic8/membrane/core/cli/RouterCLI.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (java)

@membrane-ci-server
Copy link
Copy Markdown

This pull request needs "/ok-to-test" from an authorized committer.

@rrayst
Copy link
Copy Markdown
Member

rrayst commented Dec 9, 2025

/ok-to-test

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistry.java (1)

18-18: Consider using specific imports instead of wildcard.

Wildcard imports can reduce code clarity and potentially lead to naming conflicts. Since this interface only needs List, consider keeping the specific import.

Apply this diff:

-import java.util.*;
+import java.util.List;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0046e1f and 55a948b.

📒 Files selected for processing (2)
  • annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistry.java (1 hunks)
  • annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistryImplementation.java (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (java)
🔇 Additional comments (7)
annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistry.java (2)

26-49: Excellent documentation for the registration lifecycle.

The Javadoc clearly documents thread-safety expectations, lifecycle requirements, and the relationship with start(). This will help prevent misuse of the API.


50-76: Javadoc correctly describes single-pass startup behavior.

The documentation has been updated to accurately reflect the implementation - start() processes currently queued events and returns, rather than blocking as a long-running consumer. This addresses the previous review feedback effectively.

annot/src/main/java/com/predic8/membrane/annot/yaml/BeanRegistryImplementation.java (5)

38-44: LinkedHashSet preserves insertion order—the core fix for load order issue.

Switching uidsToActivate to LinkedHashSet (line 44) ensures that activationRun() processes beans in the order they were registered, directly addressing issue #2413 where API load order was scrambled. The clarifying comments on lines 38, 43, and 44 document which structures preserve order and which don't.

Note on thread-safety: LinkedHashSet is not thread-safe, but the Javadoc correctly documents that modification of this set occurs only within the single-threaded execution context of start() and handle(BeanDefinition bd) (called from start()). The public handle(WatchAction, ...) methods only enqueue to the thread-safe BlockingQueue, so the design is sound as long as callers honor the documented contract.


51-80: Registration implementation correctly defers activation.

The implementation properly enqueues initialization events without performing activation work, as documented. The StaticConfigurationLoaded marker event signals the end of the registration phase, ensuring start() can perform deterministic, ordered activation.


82-127: start() implementation correctly processes queued events in order.

The implementation processes all currently queued events and then returns, matching the documented single-pass behavior. The StaticConfigurationLoaded marker triggers activationRun() which iterates over uidsToActivate in the insertion order preserved by LinkedHashSet, ensuring deterministic startup sequencing.


140-170: Thread-safe event queueing with ordered processing.

The public handle(WatchAction, ...) methods (lines 140-150) safely enqueue events from multiple threads. The private handle(BeanDefinition bd) (lines 160-170) is invoked only by start() in a single-threaded context, safely modifying uidsToActivate and triggering immediate activation when the queue is empty.


172-201: Ordered activation preserves registration sequence.

The iteration over uidsToActivate at line 174 respects the insertion order maintained by LinkedHashSet, ensuring APIs are activated in the sequence they were registered. This directly solves the issue reported in #2413 where startup logs showed APIs starting in scrambled order (3, 1, 2) instead of the defined order (1, 2, 3).

@rrayst rrayst merged commit c2f9a69 into master Dec 10, 2025
3 of 4 checks passed
@rrayst rrayst deleted the load-order-fix-2413 branch December 10, 2025 09:51
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.

Sequence of loaded APIs from YAML gets messed up

2 participants