-
Notifications
You must be signed in to change notification settings - Fork 2
feat: scoped tasks #248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat/PLAT-347/migrations
Are you sure you want to change the base?
feat: scoped tasks #248
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the 📝 WalkthroughWalkthroughThis pull request extends task-related API types and infrastructure to support multi-tenant scope and entity identification. Tasks now carry Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
64e7057 to
24aad70
Compare
9ff3ccb to
83955e8
Compare
8b2d7e1 to
278a4cb
Compare
83955e8 to
7391dab
Compare
278a4cb to
d35513b
Compare
7391dab to
53678c0
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this 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 (4)
server/internal/orchestrator/swarm/pgbackrest_restore.go (1)
94-97: Bug:handleErrorreturns wrong error variable.The closure returns
err(the error fromstartTaskon line 93) instead ofcause(the parameter passed tohandleError). This means all error returns fromhandleErrorcalls will return the originalstartTaskerror rather than the actual failure cause.🐛 Proposed fix
handleError := func(cause error) error { p.failTask(logger, taskSvc, t, cause) - return err + return cause }server/internal/task/service.go (2)
274-286: Bug: EmptyStatusesslice filters out all tasks.When
opts.Statusesis empty,slices.Contains(opts.Statuses, task.Status)always returnsfalse, causing all tasks to be filtered out. The check should only apply when statuses are specified.Proposed fix
func matchesFilters(task *Task, opts TaskListOptions) bool { if opts.Type != "" && task.Type != opts.Type { return false } - if !slices.Contains(opts.Statuses, task.Status) { + if len(opts.Statuses) > 0 && !slices.Contains(opts.Statuses, task.Status) { return false } if opts.NodeName != "" && (task == nil || task.NodeName != opts.NodeName) { return false } return true }
118-127: Conditional assignment forDatabaseIDbased on scope.Setting
DatabaseIDunconditionally toentityIDwill store incorrect data when the scope is notScopeDatabase(e.g., for host-scoped tasks,DatabaseIDwould contain a host ID).Proposed fix
stored := &StoredTaskLogEntry{ Scope: scope, EntityID: entityID, - DatabaseID: entityID, // For backward compatibility with database-scoped tasks + DatabaseID: "", // Set below for backward compatibility TaskID: taskID, EntryID: entryID, Timestamp: timestamp, Message: entry.Message, Fields: entry.Fields, } + if scope == ScopeDatabase { + stored.DatabaseID = entityID + }server/internal/workflows/common.go (1)
176-196: Bug: Error logged unconditionally regardless oflogTaskEventoutcome.Line 194 logs an error message even when
errisnil, resulting in misleading log entries on successful task event logging.🐛 Proposed fix
err := w.logTaskEvent(cleanupCtx, scope, entityID, taskID, task.LogEntry{ Message: "task successfully canceled", Fields: map[string]any{"status": "canceled"}, }) - logger.With("error", err).Error("failed to log task event") + if err != nil { + logger.With("error", err).Error("failed to log task event") + } }
🤖 Fix all issues with AI agents
In `@server/internal/migrate/migrations/add_task_scope.go`:
- Around line 66-71: The error message for the Etcd query uses the wrong
wording: change the fmt.Errorf call that currently returns "failed to query for
old tasks: %w" (in the block using oldTaskLogsPrefix / oldTaskLogsRangeOp /
oldTaskLogs) to a correct message like "failed to query for old task logs: %w"
so the log accurately reflects the operation querying oldStoredTaskLogEntry
entries.
🧹 Nitpick comments (7)
server/internal/task/task_log_writer.go (1)
12-16: Consider renamingDatabaseIDtoEntityIDfor clarity.The
TaskLogWriterstruct fieldDatabaseIDnow holds the genericentityIDvalue. While this maintains backward compatibility, it could cause confusion since the field name no longer accurately describes its contents for non-database scopes (e.g., host-scoped tasks).If backward compatibility is essential, consider adding a comment explaining this or introducing an
EntityIDfield alongside for future use.♻️ Optional: Add clarifying comment or alias
type TaskLogWriter struct { - DatabaseID string + DatabaseID string // Legacy name; holds entityID for all scopes TaskID uuid.UUID writer *utils.LineWriter }Or for a cleaner approach in a future refactor:
type TaskLogWriter struct { - DatabaseID string + EntityID string TaskID uuid.UUID writer *utils.LineWriter }Also applies to: 18-31
server/internal/task/task_scope_test.go (1)
26-35: Consider adding host scope test case.The test validates
EntityID()for database scope. Consider adding a similar test for host scope to ensure complete coverage of theEntityID()method's switch statement.♻️ Suggested addition
func TestOptionsWithScope(t *testing.T) { t.Run("database scope", func(t *testing.T) { opts := task.Options{ Scope: task.ScopeDatabase, DatabaseID: "my-database", Type: task.TypeCreate, } assert.Equal(t, task.ScopeDatabase, opts.Scope) assert.Equal(t, "my-database", opts.EntityID()) }) t.Run("host scope", func(t *testing.T) { opts := task.Options{ Scope: task.ScopeHost, HostID: "host-1", Type: task.TypeRemoveHost, } assert.Equal(t, task.ScopeHost, opts.Scope) assert.Equal(t, "host-1", opts.EntityID()) }) }server/internal/migrate/migrations/add_task_scope.go (1)
73-78: Consider adding idempotency logging for task logs.For consistency with the task migration pattern (lines 54-64), consider checking for
ErrAlreadyExistson task log migration and logging a skip message. This would provide better observability during repeated migration runs.Proposed change
for _, oldTaskLog := range oldTaskLogs { - err := taskStore.TaskLogMessage.Put(oldTaskLog.convert()).Exec(ctx) - if err != nil { + err := taskStore.TaskLogMessage.Create(oldTaskLog.convert()).Exec(ctx) + switch { + case errors.Is(err, storage.ErrAlreadyExists): + logger.Info(). + Stringer("entry_id", oldTaskLog.EntryID). + Stringer("task_id", oldTaskLog.TaskID). + Msg("task log entry has already been migrated, skipping") + case err != nil: return fmt.Errorf("failed to migrate task log entry %s for task %s: %w", oldTaskLog.EntryID, oldTaskLog.TaskID, err) } }Note: This assumes
TaskLogMessagehas aCreatemethod similar to tasks. If onlyPutis available, the current approach is acceptable since the data is identical on re-runs.server/internal/workflows/activities/log_task_event.go (1)
37-46: Update logger key to reflect scope-agnostic entity.The logger key
"database_id"is misleading for non-database scopes (e.g., host-scoped tasks). Consider updating to"entity_id"for consistency with the new scoped model.Proposed fix
func (a *Activities) LogTaskEvent(ctx context.Context, input *LogTaskEventInput) (*LogTaskEventOutput, error) { - logger := activity.Logger(ctx).With("database_id", input.EntityID) + logger := activity.Logger(ctx).With("scope", input.Scope, "entity_id", input.EntityID) logger.Debug("logging task event")server/internal/api/apiv1/convert.go (1)
568-572: Consider using consistent Scope conversion.In
taskToAPI(line 539),Scopeis converted usingstring(t.Scope), while here it usest.Scope.String(). Both work sinceScopeis a string type alias, but using the same pattern improves consistency.♻️ Optional: Use consistent conversion pattern
return &api.TaskLog{ - Scope: t.Scope.String(), + Scope: string(t.Scope), EntityID: t.EntityID,api/apiv1/design/task.go (2)
13-16: Consider addingEnumconstraint forscope.The description states scope can be "database or host", but there's no validation constraint. Adding an enum would prevent invalid values and provide better API documentation.
♻️ Suggested improvement
g.Attribute("scope", g.String, func() { + g.Enum("database", "host") g.Description("The scope of the task (database or host).") g.Example("database") })
101-104: AddEnumconstraint for consistency with Task type.Same recommendation as for the
Tasktype—addingg.Enum("database", "host")would ensure validation consistency across both types.♻️ Suggested improvement
g.Attribute("scope", g.String, func() { + g.Enum("database", "host") g.Description("The scope of the task (database or host).") g.Example("database") })
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (10)
api/apiv1/gen/control_plane/service.gois excluded by!**/gen/**api/apiv1/gen/control_plane/views/view.gois excluded by!**/gen/**api/apiv1/gen/http/control_plane/client/encode_decode.gois excluded by!**/gen/**api/apiv1/gen/http/control_plane/client/types.gois excluded by!**/gen/**api/apiv1/gen/http/control_plane/server/encode_decode.gois excluded by!**/gen/**api/apiv1/gen/http/control_plane/server/types.gois excluded by!**/gen/**api/apiv1/gen/http/openapi.jsonis excluded by!**/gen/**api/apiv1/gen/http/openapi.yamlis excluded by!**/gen/**api/apiv1/gen/http/openapi3.jsonis excluded by!**/gen/**api/apiv1/gen/http/openapi3.yamlis excluded by!**/gen/**
📒 Files selected for processing (32)
api/apiv1/design/task.gochanges/unreleased/Added-20260114-173755.yamlserver/internal/api/apiv1/convert.goserver/internal/api/apiv1/post_init_handlers.goserver/internal/migrate/all_migrations.goserver/internal/migrate/migrations/add_task_scope.goserver/internal/orchestrator/swarm/pgbackrest_restore.goserver/internal/task/service.goserver/internal/task/service_scoped_test.goserver/internal/task/task.goserver/internal/task/task_log_store.goserver/internal/task/task_log_store_test.goserver/internal/task/task_log_writer.goserver/internal/task/task_scope_test.goserver/internal/task/task_store.goserver/internal/task/task_store_test.goserver/internal/workflows/activities/apply_event.goserver/internal/workflows/activities/create_pgbackrest_backup.goserver/internal/workflows/activities/log_task_event.goserver/internal/workflows/activities/update_task.goserver/internal/workflows/common.goserver/internal/workflows/create_pgbackrest_backup.goserver/internal/workflows/delete_database.goserver/internal/workflows/failover.goserver/internal/workflows/pgbackrest_restore.goserver/internal/workflows/refresh_current_state.goserver/internal/workflows/restart_instance.goserver/internal/workflows/service.goserver/internal/workflows/start_instance.goserver/internal/workflows/stop_instance.goserver/internal/workflows/switchover.goserver/internal/workflows/update_database.go
🧰 Additional context used
📓 Path-based instructions (5)
server/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
server/**/*.go: Usesamber/doinjector for dependency injection; each package should have aProvide()function that registers dependencies
Use structured JSON logging with zerolog throughout the codebase, with pretty-printing enabled in dev mode
Domain-specific errors should be defined in each package; API errors should be mapped to HTTP status codes via Goa
Files:
server/internal/task/task_log_store_test.goserver/internal/workflows/refresh_current_state.goserver/internal/task/task_scope_test.goserver/internal/task/service_scoped_test.goserver/internal/migrate/all_migrations.goserver/internal/workflows/activities/log_task_event.goserver/internal/orchestrator/swarm/pgbackrest_restore.goserver/internal/task/task_log_writer.goserver/internal/workflows/activities/create_pgbackrest_backup.goserver/internal/api/apiv1/post_init_handlers.goserver/internal/migrate/migrations/add_task_scope.goserver/internal/workflows/activities/update_task.goserver/internal/workflows/service.goserver/internal/workflows/update_database.goserver/internal/workflows/activities/apply_event.goserver/internal/workflows/start_instance.goserver/internal/workflows/create_pgbackrest_backup.goserver/internal/task/task_store_test.goserver/internal/workflows/restart_instance.goserver/internal/task/task_store.goserver/internal/workflows/delete_database.goserver/internal/workflows/switchover.goserver/internal/workflows/failover.goserver/internal/workflows/stop_instance.goserver/internal/api/apiv1/convert.goserver/internal/task/task.goserver/internal/task/task_log_store.goserver/internal/workflows/common.goserver/internal/workflows/pgbackrest_restore.goserver/internal/task/service.go
server/internal/workflows/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
server/internal/workflows/**/*.go: Workflows represent long-running operations and should persist state to etcd for durability and resumability using thecschleiden/go-workflowsframework
Workflow definitions should be placed inserver/internal/workflows/and use activities fromserver/internal/workflows/activities/, registered in theActivities.Registermethod
Files:
server/internal/workflows/refresh_current_state.goserver/internal/workflows/activities/log_task_event.goserver/internal/workflows/activities/create_pgbackrest_backup.goserver/internal/workflows/activities/update_task.goserver/internal/workflows/service.goserver/internal/workflows/update_database.goserver/internal/workflows/activities/apply_event.goserver/internal/workflows/start_instance.goserver/internal/workflows/create_pgbackrest_backup.goserver/internal/workflows/restart_instance.goserver/internal/workflows/delete_database.goserver/internal/workflows/switchover.goserver/internal/workflows/failover.goserver/internal/workflows/stop_instance.goserver/internal/workflows/common.goserver/internal/workflows/pgbackrest_restore.go
server/internal/orchestrator/swarm/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
Docker Swarm integration should use services for Postgres instances, overlay networks for database isolation, and bind mounts for configuration and data directories
Files:
server/internal/orchestrator/swarm/pgbackrest_restore.go
server/internal/api/apiv1/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
Implement generated Goa service interface methods in
server/internal/api/apiv1/after regenerating code
Files:
server/internal/api/apiv1/post_init_handlers.goserver/internal/api/apiv1/convert.go
api/apiv1/design/**/*.go
📄 CodeRabbit inference engine (CLAUDE.md)
API endpoints should be defined using Goa's DSL in
api/apiv1/design/with separate files for domain-specific types (database.go, host.go, cluster.go), then regenerated withmake -C api generate
Files:
api/apiv1/design/task.go
🧠 Learnings (4)
📚 Learning: 2026-01-14T16:43:14.333Z
Learnt from: CR
Repo: pgEdge/control-plane PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T16:43:14.333Z
Learning: Applies to server/internal/api/apiv1/**/*.go : Implement generated Goa service interface methods in `server/internal/api/apiv1/` after regenerating code
Applied to files:
server/internal/task/service_scoped_test.go
📚 Learning: 2026-01-14T16:43:14.333Z
Learnt from: CR
Repo: pgEdge/control-plane PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T16:43:14.333Z
Learning: Applies to server/internal/orchestrator/swarm/**/*.go : Docker Swarm integration should use services for Postgres instances, overlay networks for database isolation, and bind mounts for configuration and data directories
Applied to files:
server/internal/orchestrator/swarm/pgbackrest_restore.go
📚 Learning: 2026-01-14T16:43:14.333Z
Learnt from: CR
Repo: pgEdge/control-plane PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T16:43:14.333Z
Learning: Applies to server/internal/resource/**/*.go : Resources should follow a standard lifecycle pattern with Refresh, Plan, and Apply phases, and declare their Executor, Dependencies, and lifecycle methods
Applied to files:
server/internal/workflows/activities/apply_event.go
📚 Learning: 2026-01-14T16:43:14.333Z
Learnt from: CR
Repo: pgEdge/control-plane PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T16:43:14.333Z
Learning: Applies to server/internal/workflows/**/*.go : Workflows represent long-running operations and should persist state to etcd for durability and resumability using the `cschleiden/go-workflows` framework
Applied to files:
server/internal/workflows/create_pgbackrest_backup.goserver/internal/workflows/switchover.goserver/internal/workflows/common.go
🧬 Code graph analysis (26)
server/internal/task/task_log_store_test.go (2)
server/internal/task/task_log_store.go (3)
NewTaskLogEntryStore(29-34)StoredTaskLogEntry(12-22)TaskLogOptions(73-76)server/internal/storage/key.go (1)
Key(19-25)
server/internal/workflows/refresh_current_state.go (1)
server/internal/task/task.go (1)
ScopeDatabase(20-20)
server/internal/task/task_scope_test.go (1)
server/internal/task/task.go (7)
ScopeDatabase(20-20)ScopeHost(21-21)Task(67-83)Scope(13-13)Options(89-99)Type(24-24)NewTask(138-163)
server/internal/task/service_scoped_test.go (3)
server/internal/task/task.go (11)
Options(89-99)Scope(13-13)ScopeDatabase(20-20)Type(24-24)TypeCreate(31-31)StatusPending(52-52)Status(45-45)ScopeHost(21-21)TypeRemoveHost(42-42)TypeUpdate(32-32)StatusRunning(53-53)server/internal/task/task_store.go (1)
TaskListOptions(55-64)server/internal/task/task_log_store.go (1)
TaskLogOptions(73-76)
server/internal/migrate/all_migrations.go (2)
server/internal/migrate/migration.go (1)
Migration(10-16)server/internal/migrate/migrations/add_task_scope.go (1)
AddTaskScope(18-18)
server/internal/workflows/activities/log_task_event.go (2)
server/internal/task/task.go (1)
Scope(13-13)server/internal/task/service.go (1)
LogEntry(103-107)
server/internal/orchestrator/swarm/pgbackrest_restore.go (2)
server/internal/task/task.go (1)
ScopeDatabase(20-20)server/internal/task/task_log_writer.go (1)
NewTaskLogWriter(18-32)
server/internal/task/task_log_writer.go (2)
server/internal/task/task.go (1)
Scope(13-13)server/internal/utils/linewriter.go (1)
NewLineWriter(19-23)
server/internal/workflows/activities/create_pgbackrest_backup.go (2)
server/internal/task/task_log_writer.go (1)
NewTaskLogWriter(18-32)server/internal/task/task.go (1)
ScopeDatabase(20-20)
server/internal/api/apiv1/post_init_handlers.go (1)
server/internal/task/task.go (1)
ScopeDatabase(20-20)
server/internal/migrate/migrations/add_task_scope.go (6)
server/internal/storage/key.go (1)
Prefix(10-16)server/internal/storage/get.go (1)
NewGetPrefixOp(93-99)server/internal/storage/errors.go (1)
ErrAlreadyExists(10-10)server/internal/task/task_store.go (1)
StoredTask(10-13)server/internal/task/task.go (2)
Scope(13-13)ScopeDatabase(20-20)server/internal/task/task_log_store.go (1)
StoredTaskLogEntry(12-22)
server/internal/workflows/activities/update_task.go (1)
server/internal/task/task.go (1)
Scope(13-13)
server/internal/workflows/update_database.go (1)
server/internal/task/task.go (2)
ScopeDatabase(20-20)Scope(13-13)
server/internal/workflows/activities/apply_event.go (5)
api/apiv1/gen/control_plane/service.go (1)
Identifier(556-556)server/internal/resource/resource.go (1)
Identifier(28-31)server/internal/resource/state.go (2)
EventTypeUpdate(20-20)EventTypeDelete(21-21)server/internal/workflows/activities/activities.go (1)
Activities(14-19)server/internal/task/task.go (1)
ScopeDatabase(20-20)
server/internal/workflows/start_instance.go (1)
server/internal/task/task.go (2)
Scope(13-13)ScopeDatabase(20-20)
server/internal/workflows/create_pgbackrest_backup.go (1)
server/internal/task/task.go (2)
ScopeDatabase(20-20)Scope(13-13)
server/internal/task/task_store_test.go (3)
server/internal/task/task_store.go (1)
NewTaskStore(20-25)server/internal/task/task.go (8)
ScopeDatabase(20-20)ScopeHost(21-21)NewTask(138-163)Options(89-99)Scope(13-13)Type(24-24)Task(67-83)Status(45-45)server/internal/storage/key.go (1)
Key(19-25)
server/internal/workflows/restart_instance.go (2)
server/internal/task/task.go (2)
ScopeDatabase(20-20)Scope(13-13)server/internal/workflows/activities/update_task.go (1)
UpdateTaskInput(16-21)
server/internal/task/task_store.go (6)
server/internal/storage/key.go (2)
Prefix(10-16)Key(19-25)server/internal/task/task.go (2)
Scope(13-13)Task(67-83)server/internal/storage/interface.go (4)
GetOp(50-52)GetMultipleOp(55-57)PutOp(65-71)DeleteOp(75-78)api/apiv1/design/task.go (1)
Task(7-78)server/internal/storage/put.go (2)
NewCreateOp(69-76)NewUpdateOp(121-128)server/internal/storage/delete.go (1)
NewDeleteKeyOp(17-23)
server/internal/workflows/switchover.go (2)
server/internal/task/task.go (2)
ScopeDatabase(20-20)Scope(13-13)server/internal/workflows/activities/update_task.go (1)
UpdateTaskInput(16-21)
server/internal/workflows/failover.go (2)
server/internal/task/task.go (2)
ScopeDatabase(20-20)Scope(13-13)server/internal/workflows/activities/update_task.go (1)
UpdateTaskInput(16-21)
server/internal/workflows/stop_instance.go (1)
server/internal/task/task.go (2)
Scope(13-13)ScopeDatabase(20-20)
server/internal/api/apiv1/convert.go (2)
server/internal/task/task.go (1)
Scope(13-13)server/internal/utils/utils.go (1)
NillablePointerTo(76-82)
server/internal/task/task_log_store.go (5)
server/internal/task/task.go (2)
Scope(13-13)ScopeDatabase(20-20)server/internal/storage/key.go (2)
Prefix(10-16)Key(19-25)server/internal/storage/interface.go (3)
GetMultipleOp(55-57)PutOp(65-71)DeleteOp(75-78)server/internal/storage/put.go (1)
NewPutOp(24-31)server/internal/storage/delete.go (1)
NewDeletePrefixOp(51-57)
server/internal/workflows/common.go (2)
server/internal/task/task.go (3)
Scope(13-13)UpdateOptions(165-174)UpdateCancel(197-202)server/internal/workflows/activities/update_task.go (1)
UpdateTaskInput(16-21)
server/internal/workflows/pgbackrest_restore.go (2)
server/internal/task/task.go (2)
ScopeDatabase(20-20)Scope(13-13)server/internal/database/spec.go (1)
Spec(252-267)
⏰ 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: Codacy Static Code Analysis
🔇 Additional comments (103)
changes/unreleased/Added-20260114-173755.yaml (1)
1-3: LGTM!The changelog entry clearly documents the new scope and entity_id fields for tasks and task logs.
server/internal/task/task_log_store_test.go (4)
14-45: LGTM!Comprehensive test coverage for key and prefix generation across both database and host scopes. The expected key formats are validated correctly.
47-104: LGTM!Good test coverage for CRUD operations with both database and host scopes. The assertions properly verify that scope and entity information is persisted and retrieved correctly.
106-166: LGTM!Delete operations are well-tested, including verification that deleting one entity's logs doesn't affect another entity's logs.
180-196: Good update to existing test for scope compatibility.The PLAT-245 test is correctly updated to include
ScopeandEntityIDfields alongside the existingDatabaseID, ensuring the test works with the new storage schema while maintaining the original test intent of verifying prefix-based isolation.server/internal/workflows/activities/create_pgbackrest_backup.go (1)
85-86: LGTM!The
NewTaskLogWritercall is correctly updated withtask.ScopeDatabasescope, which is appropriate for database backup operations.server/internal/orchestrator/swarm/pgbackrest_restore.go (2)
138-140: LGTM!The
GetTaskcall is correctly updated withtask.ScopeDatabasescope for database restore operations.
272-273: LGTM!The
NewTaskLogWritercall is correctly updated withtask.ScopeDatabasescope for database restore log streaming.server/internal/task/task.go (6)
13-22: LGTM!The
Scopetype with its constants is well-defined. TheString()method enables clean integration with storage key generation and logging.
67-83: LGTM!The
Taskstruct appropriately includes both the newScope/EntityIDfields for the new addressing model and retainsDatabaseID/HostIDfor backward compatibility during migration.
101-110: LGTM!The
EntityID()method correctly derives the appropriate entity identifier based on the scope. The empty string default is safe since validation rejects invalid scopes before this method is called inNewTask.
112-136: LGTM!The validation logic is thorough - requiring scope, enforcing scope-specific ID requirements, and properly rejecting invalid scopes with descriptive error messages.
148-162: LGTM!
NewTaskcorrectly initializesScopeandEntityIDfrom the validated options, ensuring the task is properly addressed.
246-254: LGTM!The
TaskLogstruct appropriately mirrors theTaskstruct with scope and entity fields, maintaining consistency across the task model.server/internal/task/task_scope_test.go (3)
11-14: LGTM!Simple and effective test verifying the
String()method for scope constants.
16-24: LGTM!Validates that
Taskstruct properly stores scope and entity fields.
37-114: LGTM!Excellent table-driven test coverage for validation logic. All edge cases are covered: valid scopes, missing required fields, scope-specific ID requirements, and invalid scope values.
server/internal/task/task_log_store.go (8)
12-22: LGTM!The
StoredTaskLogEntrystruct appropriately includes scope and entity fields while retainingDatabaseIDfor backward compatibility.
36-49: LGTM!The prefix hierarchy is well-structured and the deprecation pattern provides a clean migration path from database-specific to entity-generic methods.
51-71: LGTM!The key generation methods are well-structured with appropriate deprecated wrappers for migration compatibility.
78-99: LGTM!The
GetAllByTaskmethod correctly uses scope and entity for key generation, with proper pagination support.
101-105: LGTM!Deprecated wrapper correctly delegates to the new scope-aware method.
107-110: LGTM!The
Putmethod correctly derives the storage key from the item's scope and entity fields.
112-121: LGTM!Delete methods follow the same scope-aware pattern with appropriate deprecated wrappers.
123-132: LGTM!Entity-level deletion methods are consistent with the overall pattern and provide appropriate deprecation wrappers.
server/internal/task/task_store.go (5)
27-33: LGTM!The
tasks_v2prefix enables non-destructive migration to the new keyspace. TheEntityPrefixmethod provides consistent scope-aware key generation.
35-42: LGTM!Key generation and retrieval methods correctly incorporate scope and entity into the storage path.
66-90: LGTM!The
GetAllByEntitymethod correctly implements scope-aware listing with proper pagination cursor handling for both ascending and descending sort orders.
92-100: LGTM!Create and Update methods correctly derive storage keys from the task's scope and entity fields.
102-109: LGTM!Delete methods correctly implement scope-aware deletion. The lack of deprecated wrappers here (unlike
task_log_store.go) suggests the migration handles task store operations differently, which aligns with the PR's idempotent migration approach that copies to the new keyspace.server/internal/task/task_store_test.go (5)
14-44: LGTM!Excellent test coverage for key generation, validating the expected key structure for both database and host scopes.
46-117: LGTM!Comprehensive CRUD test coverage including create, retrieve, and update operations for both database and host scopes. Good use of unique store roots for test isolation.
119-191: LGTM!Thorough test coverage for
GetAllByEntity, verifying correct entity-level isolation and accurate task counts for both scopes.
193-255: LGTM!Good delete test coverage verifying that
DeleteByEntityremoves only the target entity's tasks while leaving other entities unaffected.
257-303: LGTM!The PLAT-245 regression test is correctly updated to use the new scope-aware methods while preserving its original purpose of verifying that prefix collisions (e.g., "database" vs "database2") are properly handled.
server/internal/migrate/migrations/add_task_scope.go (2)
102-145: LGTM!The conversion functions correctly map legacy task data to the new scoped model. Setting
Scopetotask.ScopeDatabaseandEntityIDtoDatabaseIDis appropriate since all existing tasks are database-scoped.
24-46: LGTM!Dependency injection follows the
samber/dopattern as per coding guidelines, and structured logging with zerolog is correctly implemented with appropriate context enrichment.server/internal/migrate/all_migrations.go (1)
3-11: LGTM!The migration is correctly registered and follows the documented pattern for adding new migrations in chronological order.
server/internal/task/service.go (2)
42-57: LGTM!The
UpdateTaskmethod correctly retrieves the task using the scope and entity ID from the task object itself, maintaining consistency with the new scoped model.
136-178: LGTM!The
GetTaskLogmethod correctly handles backward compatibility by conditionally populating legacy fields (DatabaseID,HostID) based on the scope. The TODO comment appropriately flags future cleanup.server/internal/workflows/activities/log_task_event.go (1)
15-20: LGTM!The input struct correctly carries
ScopeandEntityIDto support the new scoped task model.server/internal/workflows/activities/apply_event.go (2)
149-166: LGTM!The renamed
logResourceEventfunction correctly usestask.ScopeDatabasesince this activity handles database resource events. The hardcoded scope is appropriate for this context.
104-127: LGTM!The call sites correctly pass
databaseIDandtaskIDtologResourceEvent, maintaining the database-scoped context for resource lifecycle events.server/internal/task/service_scoped_test.go (7)
14-41: LGTM! Good test coverage for database-scoped task creation and retrieval.The test properly validates that:
Scopeis set toScopeDatabaseEntityIDis populated fromDatabaseID- Both fields are persisted and retrievable
43-65: LGTM! Good coverage for host-scoped tasks.Validates the alternative scope path where
EntityIDis derived fromHostIDinstead ofDatabaseID.
67-118: LGTM! Comprehensive test for task isolation across scopes and entities.Good coverage for verifying that tasks are correctly partitioned by scope and entity ID, ensuring no cross-contamination between different databases and hosts.
120-158: LGTM! Task log operations properly tested.The test correctly handles JSON serialization behavior (int → float64 conversion) and validates multi-entry log scenarios.
160-182: LGTM! Update task flow validated.Tests the status progression from
StatusPendingtoStatusRunningviaStart()within the scoped context.
184-227: LGTM! Delete operations properly tested.Good coverage for both single task deletion (with
ErrTaskNotFoundverification) and bulk deletion viaDeleteAllTasks.
229-290: LGTM! Task log deletion operations well covered.Tests both single-task log deletion and bulk log deletion for all tasks under a scope/entity pair.
server/internal/workflows/service.go (13)
51-58: LGTM! Scope correctly applied to CreateDatabase workflow.The
DeleteAllTasksandCreateTaskcalls properly include the database scope.
77-80: LGTM! UpdateDatabase task creation includes scope.
100-103: LGTM! DeleteDatabase task creation includes scope.
128-131: LGTM! CreatePgBackRestBackup task creation includes scope.
160-163: LGTM! PgBackRestRestore properly scopes both parent and child tasks.Both the main restore task and per-node restore tasks correctly include the database scope.
Also applies to: 173-178
225-235: LGTM! abortTasks correctly uses task's own Scope and EntityID.This is the right approach - using the task's fields rather than hardcoding values allows flexibility if different scopes are used in the future.
272-276: LGTM! RestartInstance task creation includes scope.
291-296: LGTM! StopInstance task creation includes scope.
311-316: LGTM! StartInstance task creation includes scope.
329-356: LGTM! CancelDatabaseTask properly updated for scope-aware operations.The function correctly:
- Retrieves the task using
ScopeDatabaseanddatabaseID- Adds log entry with scope parameters
- Uses the renamed
workflowInstancevariable (improved naming)
363-368: LGTM! SwitchoverDatabaseNode task creation includes scope.
384-389: LGTM! FailoverDatabaseNode task creation includes scope.
411-414: LGTM! RemoveHost creates database-scoped tasks for each affected database.This is correct - the RemoveHost workflow creates update tasks scoped to each database being modified.
server/internal/workflows/refresh_current_state.go (2)
65-72: LGTM! logTaskEvent call updated with database scope.
80-90: LGTM! Second logTaskEvent call updated with database scope.server/internal/workflows/start_instance.go (3)
32-37: LGTM! Error handler UpdateTaskInput includes scope and entity.
45-50: LGTM! Start task UpdateTaskInput includes scope and entity.
67-72: LGTM! Completion task UpdateTaskInput includes scope and entity.server/internal/api/apiv1/convert.go (1)
539-541: LGTM! Task API mapping includes new scope and entity fields.The conversion correctly:
- Maps
Scopeas a string- Maps
EntityIDdirectly- Makes
DatabaseIDnullable for non-database scopesserver/internal/workflows/stop_instance.go (1)
32-37: LGTM! Scope migration looks correct.All three
UpdateTaskInputconstructions correctly usetask.ScopeDatabaseandinput.DatabaseIDas theEntityID, aligning with the new scoped task model.Minor observation: The error handler at line 38 uses
ExecuteUpdateTaskdirectly, while the completion path at line 73 uses thew.updateTaskhelper. This inconsistency exists in the original code and isn't introduced by this PR, but could be unified in a future refactor.Also applies to: 45-50, 67-72
server/internal/workflows/create_pgbackrest_backup.go (2)
44-44: LGTM! Cancellation path correctly updated with scope.The
cancelTaskcall now properly passestask.ScopeDatabaseas the scope parameter, maintaining consistency with the new scoped task model.
53-58: LGTM! All UpdateTaskInput constructions correctly migrated.All three task update paths (error handling, start, and complete) consistently use
Scope: task.ScopeDatabaseandEntityID: input.DatabaseID, aligning with the broader scope-aware refactoring.Also applies to: 74-79, 126-131
server/internal/workflows/delete_database.go (2)
41-41: LGTM! Cancellation cleanup correctly updated.The
cancelTaskinvocation in the deferred cleanup block correctly passestask.ScopeDatabaseas the scope parameter.
60-65: LGTM! Scope-based task updates correctly implemented.All
UpdateTaskInputconstructions in the error handler, start, and completion paths consistently usetask.ScopeDatabaseandinput.DatabaseIDasEntityID.Also applies to: 71-76, 109-114
server/internal/workflows/restart_instance.go (2)
47-47: LGTM! Cancellation path updated with scope.The
cancelTaskcall correctly includestask.ScopeDatabaseas the scope parameter in the deferred cleanup block.
54-59: LGTM! UpdateTaskInput constructions correctly migrated.All task update paths consistently use the new
ScopeandEntityIDfields with appropriate values (task.ScopeDatabaseandinput.DatabaseID).Also applies to: 67-72, 87-92
server/internal/workflows/switchover.go (2)
57-57: LGTM! Cancellation cleanup correctly scoped.The
cancelTaskcall in the deferred cleanup block correctly passestask.ScopeDatabasealong within.DatabaseIDandin.TaskID.
63-68: LGTM! All four UpdateTaskInput paths correctly updated.All task update constructions—error handler, start, early completion (when candidate is already leader), and final completion—consistently use
Scope: task.ScopeDatabaseandEntityID: in.DatabaseID.Also applies to: 74-79, 142-147, 166-171
server/internal/workflows/activities/update_task.go (2)
16-21: LGTM! Clean struct refactoring for scope-based task identification.The
UpdateTaskInputstruct now correctly usesScopeandEntityIDto identify task parents, replacing the previousDatabaseIDfield. This aligns with the PR objective to support non-database entities.
38-51: LGTM! Logger and service call updated consistently.The logger context and
GetTaskcall are correctly updated to use the new scope-based parameters.server/internal/workflows/update_database.go (4)
45-45: LGTM! Cancellation path updated with scope parameter.The
cancelTaskcall now correctly passestask.ScopeDatabaseas the scope parameter.
66-72: LGTM! Error handler uses scope-based task identification.The
UpdateTaskInputinhandleErrorcorrectly usestask.ScopeDatabaseandinput.Spec.DatabaseIDasEntityID.
77-82: LGTM! Task start update uses scope-based identification.Consistent with the new scope model.
133-138: LGTM! Task completion update uses scope-based identification.All
UpdateTaskInputinstances in this workflow are consistently updated.server/internal/workflows/common.go (1)
151-174: LGTM!logTaskEventcorrectly updated for scope-based task identification.The function signature and
LogTaskEventInputconstruction properly usescopeandentityIDparameters.server/internal/api/apiv1/post_init_handlers.go (6)
607-607: LGTM! Scope parameter added to switchover task check.The
GetTaskscall now correctly passestask.ScopeDatabaseto ensure proper scope-based filtering.
700-700: LGTM! Scope parameter added to failover task check.Consistent with the switchover handler pattern.
731-731: LGTM!ListDatabaseTasksuses scope-based retrieval.Correctly scoped to database tasks.
751-751: LGTM!GetDatabaseTaskuses scope-based retrieval.
769-769: LGTM! Task log retrieval uses scope-based lookups.Both the task existence check and log retrieval correctly use
task.ScopeDatabase.Also applies to: 779-779
1033-1033: LGTM!CancelDatabaseTaskuses scope-based task lookup.Correctly scoped for database task cancellation.
server/internal/workflows/failover.go (5)
41-41: LGTM! Cancellation cleanup uses scope-based task cancellation.The
cancelTaskcall correctly passestask.ScopeDatabaseas the scope.
47-52: LGTM! Error handler uses scope-based task update.
UpdateTaskInputcorrectly usesScopeandEntityIDfields.
57-62: LGTM! Task start update uses scope-based identification.Consistent with the new scope model.
142-148: LGTM! Early completion path (candidate is leader) uses scope-based update.Correctly handles the skip-failover scenario with proper scope fields.
165-170: LGTM! Final completion update uses scope-based identification.All
UpdateTaskInputinstances in the failover workflow are consistently updated.server/internal/workflows/pgbackrest_restore.go (3)
30-36: LGTM!The deferred cancel handler correctly passes the new
scopeandentityIDparameters tocancelTask, consistent with the updated signature incommon.go.
52-57: LGTM!The
UpdateTaskInputconstructions for both task start and completion correctly populateScopeandEntityIDfields, replacing the previousDatabaseIDapproach.Also applies to: 106-111
138-156: LGTM!The error handler correctly updates both node tasks and the main task with the new
ScopeandEntityIDfields, maintaining consistency across all failure paths.api/apiv1/design/task.go (4)
17-20: LGTM!The
entity_idfield correctly omits a UUID format constraint since it can hold either a database ID (UUID) or host ID (string), as reflected in the examples.
113-116: Verify intent: newly added field marked as deprecated.The
host_idattribute appears to be newly introduced in this PR but is immediately marked as deprecated. If this is intentional for backward compatibility purposes, consider clarifying this in the description. Otherwise, the deprecation notice may have been added in error.
66-66: LGTM!The required fields now properly mandate
scopeandentity_id, reflecting the shift fromdatabase_id-based to scope-based task identification. This is a breaking API change that aligns with the PR objectives.
266-313: LGTM!The
ListDatabaseTasksResponseexamples properly demonstrate both the newscope/entity_idfields alongside the existingdatabase_idfield, which aids API consumers during the transition.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
8619be9 to
baedea3
Compare
d35513b to
b81f4bf
Compare
Adds a "scope" concept to tasks to support tasks that apply to non- database entities. With this change, the task's parent is identified by the `scope` and `entity_id` fields. This changes the way that tasks and task logs are stored, so we'll add a migration to move existing tasks in a subsequent commit. PLAT-347
Adds a migration to copy logs from their old location to the new keyspace that includes the task scope. This change is non-destructive to allow the possibility of a rollback. Tasks are updated throughout their lifetime, so this migration will not overwrite tasks that have already been migrated. PLAT-347
Some small bug fixes that came up during code review - `handleError` returns wrong error variable in `swarm/pgbackrest_restore.go` - empty `Statuses` slice filters out all tasks in `task/service.go` - error logged unconditionally regardless of `logTaskEvent` outcome in `workflows/common.go`
baedea3 to
1ad0c8b
Compare
Summary
Adds a "scope" concept to tasks to support tasks that apply to non-database entities. With this change, the task's parent is identified by the
scopeandentity_idfields.This changes how tasks and task logs are stored, so this PR includes a non-destructive, idempotent migration to move tasks and task logs to a new keyspace.
Changes
scopeandentity_idfields to tasks and task logsTesting
mainbranchscopeandentity_idfields exist and are set correctly on all tasksPLAT-347
Summary by CodeRabbit
New Features
Refactor
Tests
✏️ Tip: You can customize this high-level summary in your review settings.