edbf78f236
- Halo Plugin 后端(Java/Gradle),含 DarkModePlugin 主类和测试 - Vue 3 + TypeScript 前端 UI,包含主题切换组件和设置页面 - 暗色模式 CSS 变量和覆盖样式(布局/编辑器/表单/滚动条等) - 设计文档和调查文档 - Halo 插件/主题开发 Agent Skills Co-Authored-By: Claude <noreply@anthropic.com>
5.2 KiB
5.2 KiB
Reconciler (Controller Pattern)
A Kubernetes-style controller that watches Extension resources and continuously reconciles their desired state. More commonly used than Watcher in production plugins.
Source: Reconciler | ControllerBuilder | Controller
Basic Pattern
@Component
public class MyReconciler implements Reconciler<Reconciler.Request> {
private final ExtensionClient client;
public MyReconciler(ExtensionClient client) {
this.client = client;
}
@Override
public Result reconcile(Request request) {
// Fetch the extension being reconciled
var myExt = client.fetch(MyExtension.class, request.name());
if (myExt.isEmpty()) {
return Result.doNotRetry();
}
// Perform reconciliation logic
var ext = myExt.get();
// ... update annotations, labels, related resources, etc.
// Update the extension if modified
client.update(ext);
return Result.doNotRetry();
}
@Override
public Controller setupWith(ControllerBuilder builder) {
return builder
.extension(new MyExtension())
.syncAllOnStart(false)
.build();
}
}
ControllerBuilder Options
| Method | Description |
|---|---|
.extension(new MyExtension()) |
The extension type to watch (required) |
.syncAllOnStart(true) |
Reconcile all existing instances on startup (default: true) |
.syncAllListOptions(listOptions) |
Filter which existing instances to sync on start |
.minDelay(Duration) |
Minimum retry delay (default: 5ms) |
.maxDelay(Duration) |
Maximum retry delay (default: 1000s) |
.workerCount(int) |
Number of concurrent workers (default: 1) |
.onAddMatcher(matcher) |
Filter which add events to process |
.onUpdateMatcher(matcher) |
Filter which update events to process |
.onDeleteMatcher(matcher) |
Filter which delete events to process |
Result Types
// Success, do not retry
return Result.doNotRetry();
// Requeue after a delay (for async operations or retry)
return Result.requeue(Duration.ofSeconds(30));
Lifecycle Integration
Reconcilers are auto-discovered by Spring. If you need to start/stop manually (e.g., conditional on plugin config):
@Component
public class MyReconciler implements Reconciler<Request>, SmartLifecycle {
private Controller controller;
private boolean running = false;
@Override
public Controller setupWith(ControllerBuilder builder) {
this.controller = builder.extension(new MyExtension()).build();
return controller;
}
@Override
public void start() {
if (controller != null && !running) {
controller.start();
running = true;
}
}
@Override
public void stop() {
if (controller != null && running) {
controller.dispose(); // or controller.stop()
running = false;
}
}
@Override
public boolean isRunning() { return running; }
}
Finalizers
Use finalizers for cleanup before an extension is deleted:
@Override
public Result reconcile(Request request) {
var ext = client.fetch(MyExtension.class, request.name()).orElse(null);
if (ext == null) return Result.doNotRetry();
// Check if being deleted
if (ext.getMetadata().getDeletionTimestamp() != null) {
// Perform cleanup
doCleanup(ext);
// Remove finalizer to allow deletion
ExtensionUtil.removeFinalizers(ext.getMetadata(), Set.of("my-plugin/finalizer"));
client.update(ext);
return Result.doNotRetry();
}
// Add finalizer if not present
ExtensionUtil.addFinalizers(ext.getMetadata(), Set.of("my-plugin/finalizer"));
client.update(ext);
// Normal reconciliation
return Result.doNotRetry();
}
When to Use Reconciler vs Watcher
| Reconciler | Watcher | |
|---|---|---|
| Use case | Continuous state reconciliation, finalizers, retries | One-time event handling |
| Persistence | Queued, survives restarts | In-memory only |
| Concurrency | Configurable worker count | Single threaded |
| Retry | Built-in exponential backoff | No retry |