Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,9 @@ This pattern ensures atomicity - either all changes succeed and are persisted, o

### Repository Abstraction

The engine doesn't directly interact with Git repositories. Instead, it:

- Opens repositories through the **cache layer**
- Works with repository abstractions that hide storage implementation details
- Delegates all storage operations to repository adapters
- Maintains separation between business logic and storage mechanisms
The engine does not directly interact with Git repositories. Instead, it opens repositories through the **cache layer** and works with
repository abstractions that hide storage implementation details. It also delegates all storage operations to repository adapters.
Additionally, it maintains separation between business logic and storage mechanisms.

### Concurrency Model

Expand All @@ -75,31 +72,21 @@ The engine uses **per-package mutexes** to prevent concurrent modifications to t

**Optimistic Locking:**

For update operations, the engine uses **Kubernetes resource versions**:

- Client must provide current resource version in update request
- Engine compares provided version with actual version
- Returns conflict error if versions don't match
- Forces client to re-read and retry with latest version
- Prevents lost updates when multiple clients modify same package
For update operations, the engine uses **Kubernetes resource versions**. The client must provide current resource version in update
request. Then the engine compares provided version with actual version and returns a conflict error if the versions do not match. This
forces the client to re-read and retry with the latest version. This way lost updates are prevented when multiple clients modify the same
package.
Comment on lines +75 to +78

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
For update operations, the engine uses **Kubernetes resource versions**. The client must provide current resource version in update
request. Then the engine compares provided version with actual version and returns a conflict error if the versions do not match. This
forces the client to re-read and retry with the latest version. This way lost updates are prevented when multiple clients modify the same
package.
For update operations, the engine uses **Kubernetes resource versions**. The client must provide current resource version in update
request. Then, the engine compares provided version with actual version and returns a conflict error if the versions do not match. This
forces the client to re-read and retry with the latest version. This way lost updates are prevented when multiple clients modify the same
package.


**Draft Isolation:**

Drafts provide natural concurrency isolation:

- Each draft is a separate workspace in the repository
- Multiple drafts can exist for different workspaces simultaneously
- Drafts don't interfere with each other until closed
- Closing a draft is an atomic operation
Drafts provide natural concurrency isolation. Each draft is a separate workspace in the repository. This means that multiple drafts can
exist for different workspaces simultaneously and drafts do not interfere with each other until closed. Closing a draft is an atomic
operation.

**Read Operations:**

List and Get operations are **lock-free**:

- Read from cached repository state
- No locking required for queries
- May see slightly stale data during cache refresh
- Eventually consistent with repository state
List and Get operations are **lock-free**, they read from cached repository state. No locking is required for queries. You may see
slightly stale data during cache refresh but eventually it will be consistent with the repository state.

**Concurrency characteristics:**
- **Single package**: Only one write operation at a time (mutex protected)
Expand Down Expand Up @@ -128,44 +115,41 @@ The Engine exposes a single interface (`CaDEngine`) with operations grouped by r
- **ObjectCache**: Access the watcher manager for real-time change notifications
- **OpenRepository**: Internal helper to access repositories through the cache

**Interface characteristics:**
**Interface characteristics**

- All operations are **context-aware** for cancellation and tracing
- Operations accept **API objects** (porchapi types) and return **repository abstractions**
- The interface is **synchronous** - operations complete before returning
- Errors are returned directly rather than stored in status fields
All operations are **context-aware** for cancellation and tracing. Operations accept **API objects** (porchapi types) and return
**repository abstractions**. The interface is **synchronous**, which means that operations complete before returning. Additionally, errors
are returned directly rather than stored in status fields.

## Package Lifecycle State Machine

The Engine enforces a strict state machine for package revision lifecycle:

![Package Lifecycle Workflow](/static/images/porch/flowchart.drawio.svg)
![Package Lifecycle Workflow](/static/images/porch/lifecycle-flowchart.drawio.svg)

### State Transition Rules

**Draft State:**
- Can be created directly or as default when no lifecycle specified
- Allows full modifications: tasks, resources, metadata, lifecycle
- Can transition to: Proposed, Published
- Cannot be created with Published or DeletionProposed lifecycle

**Proposed State:**
- Indicates package revision is ready for review
- Allows modifications like Draft
- Can transition to: Draft (for rework), Published (for approval)
- Typically used in approval workflows

**Published State:**
- **Immutable** - no resource or task modifications allowed
- Only metadata (labels, annotations) and lifecycle can be updated
- Can transition to: DeletionProposed
- Represents the deployed, production-ready state

**DeletionProposed State:**
- Marks package revision for deletion
- Considered "published" for lifecycle checks
- Final state before actual deletion
- Used to signal intent to remove while maintaining audit trail
#### Draft State

This state can be created directly, or as default when no lifecycle is specified. It allows full modifications of tasks, resources,
metadata or lifecycle. From draft state packages can transition to either Proposed or Published. However, they cannot be created with
Published or DeletionProposed lifecycle.
Comment on lines +132 to +136

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

id reword this. not of your rewording that is the problem here its just the original information was not so accurate. draft can only move into proposed state or be directly deleted (which is not a state). the last sentence also of "However, they cannot be created with Published or DeletionProposed lifecycle." makes 0 sense to me. remove it

Comment on lines +134 to +136

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
This state can be created directly, or as default when no lifecycle is specified. It allows full modifications of tasks, resources,
metadata or lifecycle. From draft state packages can transition to either Proposed or Published. However, they cannot be created with
Published or DeletionProposed lifecycle.
This is the starting state of a package when created and the default when no lifecycle is specified.
It allows full modifications of tasks, resources, metadata or lifecycle.
Packages in this state can transition to either Proposed or Published.


#### Proposed State

This state indicates that the package revision is ready for review. From the proposed state, packages can transition to either Draft
(for rework) or Published (for approval). This is typically used in approval workflows.
Comment on lines +138 to +141

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

maybe we can reword "for approval" to something else? the proposed state is the "check for approval state" the published is it passed and was approved state. just reads a bit off for me

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

(last sentence) its not that this is typically used in approval workflows. it is solely used to act as the approval workflow state. just get rid of the last sentence there.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i think this state is also immutable much like the published state. mutability can only happen in draft state. hence why a user must reject the proposed package to send it back to draft to make changes.
This is also stated here in the docs https://deploy-preview-1089--kpt-porch.netlify.app/docs/4_tutorials_and_how-tos/working_with_package_revisions/creating-packages/#step-5-propose-the-package-revision


#### Published State

Packages in this state are immutable, meaning that no resource or task modifications are allowed to them. Only metadata (labels,
annotations) and lifecycle can be updated. From the published state, packages can transition to DeletionProposed. The published state
represents the deployed, production-ready state.
Comment on lines +145 to +147

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
Packages in this state are immutable, meaning that no resource or task modifications are allowed to them. Only metadata (labels,
annotations) and lifecycle can be updated. From the published state, packages can transition to DeletionProposed. The published state
represents the deployed, production-ready state.
Packages in this state are immutable, meaning that no resource or task modifications are allowed to them. Only metadata (labels,
annotations) and lifecycle can be updated. From the published state, packages can transition to DeletionProposed. The published state
represents the deployable, production-ready state.


#### DeletionProposed State

This state marks the package revision for deletion. For lifecycle checks, packages in these state are considered "published". This is the
final state before actual deletion and it is used to signal intent to remove the package while maintaining audit trail.

### Lifecycle Enforcement

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,16 +271,11 @@ Repository.UpdatePackageRevision
Return Draft Handle
```

**Draft from existing:**
- Loads current package revision content
- Creates mutable draft workspace
- Copies resources to draft for modification
- Preserves original revision (immutable)
In this case, the current package revision of the content is loaded and a mutable draft
workspace is created. Resources are copied to draft for modification, while the original revision is preserved (immutable)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
workspace is created. Resources are copied to draft for modification, while the original revision is preserved (immutable)
workspace is created. Resources are copied to draft for modification, while the original revision is preserved (immutable).


**Draft operations:**
- Same as creation draft
- Can modify resources, lifecycle, metadata
- Changes isolated until closed
**Draft operations:** The same as creation draft, meaning resources, lifecycle and metadata can be modified.
The changes are isolated until closed.
Comment on lines -280 to +278

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't know what "same as creation draft" was meant to mean here but it sounds wrong. Maybe "same as an init draft" or "same as draft creation"?

If anybody has any idea, please chime in.


### Mutation Application

Expand All @@ -303,11 +298,8 @@ TaskHandler.DoPRMutations
- **Lifecycle changes**: Handled separately (UpdateLifecycle)
- **Metadata changes**: Handled after draft closure

**Mutation process:**
- Compares old and new package revision specs
- Identifies new tasks to apply
- Delegates to task handler for execution
- Returns modified draft
Old and new package revision specs are compared and new tasks to apply are identified. These tasks are delegated to the
task handler for execution after which a modified draft is returned.
Comment on lines +301 to +302

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We should mention that there can be only 1 task now, the "creation source" (init/copy/clone/upgrade). I assume this part of the docs was not updated when we made it a single-element list

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i think this was being discussed at the time of the creation or there was some very odd case where you could have more than one in the task list. i don't remember fully but if its not true anymore it should be altered


### Metadata-Only Update Path

Expand All @@ -325,16 +317,11 @@ UpdatePackageRevision (Published/DeletionProposed)
Return Updated PR
```

**Metadata-only updates:**
- For Published and DeletionProposed package revisions
- No draft-commit workflow needed
- Direct metadata update on immutable revision
- Only labels, annotations, finalizers, owner references
These are for Published and DeletionProposed package revisions, no draft-commit workflow is needed. This means direct metadata
update on immutable revision. Only labels, annotations, finalizers, owner references are updated.

**Why metadata-only:**
- Published packages are immutable (content cannot change)
- Metadata doesn't affect package content
- Allows operational updates without content changes
Published packages are immutable, meaning that the content cannot change. Metadata, however, does not affect package content.
This allows operational updates without content changes.

## Update Package Resources Workflow

Expand Down Expand Up @@ -401,21 +388,17 @@ TaskHandler.DoPRResourceMutations
Return RenderStatus
```

**Resource mutation process:**
- Updates package resource content directly
- Executes render task (runs KRM functions)
- Returns render status with function results
- No lifecycle change (resources updated in-place)

**Render execution:**
- Runs configured KRM function pipeline
- Functions can validate, transform, generate resources
- Results returned in RenderStatus
- **Render failure handling**:
- Default behavior: Render errors prevent draft closure (no resources persisted)
- With `porch.kpt.dev/push-on-render-failure: "true"` annotation: Draft is closed even on render failure
- The behavior of partially-rendered resources can be further controlled via Kptfile annotations (see [kpt documentation](https://kpt.dev/book/04-using-functions/#debugging-render-failures))
- Error is always returned to caller regardless of persistence behavior
Package resource content is updated directly and the render task is executed by running KRM functions. Once done,
the render status with function results is returned. This does not involve a lifecycle change, as the resources are updated in-place.

The render is executed by running a configured KRM function pipeline. These functions can validate, transform and generate resources.
The results are returned in RenderStatus.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
The results are returned in RenderStatus.
The results are returned in a `RenderStatus`.

The RenderStatus is a data type


In case of render failure, the default behavior is for render errors to prevent draft closure (no resources persisted).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
In case of render failure, the default behavior is for render errors to prevent draft closure (no resources persisted).
In case of render failure, the default behavior is for render errors to prevent draft finalization (no resources persisted).

With the `porch.kpt.dev/push-on-render-failure: "true"` annotation, the draft is closed even on render failure. The behavior of
partially-rendered resources can be further controlled via Kptfile annotations
(see [kpt documentation](https://kpt.dev/book/04-using-functions/#debugging-render-failures)). The error is always returned to
caller regardless of persistence behavior.

### Persisting Resources on Render Failure

Expand All @@ -439,12 +422,13 @@ The `porch.kpt.dev/push-on-render-failure` annotation enables saving work-in-pro
kubectl annotate packagerevision <name> porch.kpt.dev/push-on-render-failure=true
```

**Important notes:**
{{% alert title="Note" color="primary" %}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

good change. thanks. makes it more readable

- Only applies to Draft PackageRevisions during resource updates (via `UpdatePackageResources`)
- Does not apply to package creation operations (init, clone, edit, copy)
- Error is always returned even when resources are persisted
- The behavior of partially-rendered resources can be further controlled via Kptfile annotations (see [kpt documentation](https://kpt.dev/book/04-using-functions/#debugging-render-failures))
- In rare cases (e.g., internal errors during resource persistence), push may be prevented regardless of the annotation
- In rare cases (for example, internal errors during resource persistence), push may be prevented regardless of the annotation
{{% /alert %}}

## Rollback Mechanism

Expand Down Expand Up @@ -490,57 +474,40 @@ When a draft is created, the engine sets up a rollback handler that will be invo
4. **Captures references** to the draft and repository for later use

The rollback handler is a closure that captures the necessary context and can be invoked automatically when errors occur during the operation.

**Rollback handler characteristics:**
- **Closure**: Captures draft and repository references
- **Best effort**: Logs warning if cleanup fails
- **Non-blocking**: Doesn't prevent error return
- **Automatic**: Invoked on any operation error
It captures draft and repository references and logs a warning if a cleanup fails. However, it is non-blocking, meaning it does not prevent error return.

### Rollback Triggers

**When rollback invoked:**
**Rollback is invoked for:**
- Task application errors
- Lifecycle update errors
- Validation errors during operation
- Any error after draft creation

**When rollback NOT invoked:**
**Rollback is NOT invoked for:**
- Draft closure errors (would likely fail again)
- Errors before draft creation (nothing to clean up)
- Metadata update errors (draft already closed)

### Rollback Limitations

**Rollback may fail:**
- Repository connection lost
- Permission errors
- Draft already closed
- Repository in inconsistent state
The rollback may fail if the repository connection is lost, due to permission errors,
if the draft is already closed, or if the repository is in an inconsistent state.

**Failure handling:**
- Warning logged with error details
- Original operation error still returned
- Draft may remain in repository
- Manual cleanup may be required
- Repository garbage collection may clean up eventually
In case of failure, the warning is logged with error details, but the original operation error is still returned.
The draft may remain in the repository, which means that manual cleanup may be required. However, the repository
garbage collection may clean up eventually.

### Rollback vs Transaction

**Not a true transaction:**
- No two-phase commit
- No distributed transaction support
- Best-effort cleanup only
This is not classified as true transaction as there is no two-phase commit and no distributed
transaction support. It is best-effort cleanup only.

**Why not a transaction:**
- Git doesn't support transactions
- Repository operations not transactional
- Rollback is cleanup, not undo
Transactions are not used as Git does not support them and repository operations are not transactional.
Additionally, rollback is a cleanup, not an undo.

**Atomicity guarantee:**
- Either package revision created/updated or not
- No partial package revisions visible to clients
- Draft isolation prevents intermediate state visibility
Atomicity is guaranteed. Package revisions are either created/updated or not. There is no partial package
revisions visible to clients, as draft isolation prevents intermediate state visibility.

## Draft Lifecycle

Expand Down Expand Up @@ -579,11 +546,9 @@ Drafts have a specific lifecycle within the workflow:

### Draft Isolation

**Isolation guarantees:**
- Draft changes not visible to other operations
- Draft workspace separate from other revisions
- Multiple drafts can exist simultaneously (different workspaces)
- Draft closure is atomic (all or nothing)
Isolation guarantees that draft changes are not visible to other operations, since the draft workspace is
separate from other revisions. This means, that multiple drafts can exist simultaneously, in different workspaces.
Draft closure is atomic (all or nothing).

**Concurrency:**
- Multiple drafts for different packages: Fully concurrent
Expand All @@ -596,42 +561,28 @@ The Engine optimizes the draft-commit workflow:

### Optimization Strategies

**Lazy draft creation:**
- Draft created only when needed
- Not created for metadata-only updates
- Not created for read operations
During lazy draft creation, a draft is created only when needed. Drafts are not created for metadata-only updates,
or for read operations.

**Early validation:**
- Validation before draft creation
- Fails fast without expensive operations
- Reduces rollback frequency
Early validation is performed before draft creation. It fails fast without expensive operations
and reduces rollback frequency.

**Efficient draft closure:**
- Single repository operation
- Atomic commit to Git
- Minimal overhead
Another optimization is efficient draft closure, which uses single repository operation,
atomic commit to Git and minimal overhead.

### Performance Characteristics

**Draft creation cost:**
- Allocates workspace in repository
- Copies resources (for updates)
- Relatively lightweight

**Draft modification cost:**
- In-memory operations
- No repository access during modifications
- Task handler does actual work

**Draft closure cost:**
- Git commit/tag creation
- Repository write operation
- Most expensive part of workflow

**Rollback cost:**
- Draft closure + deletion
- Two repository operations
- Only on error path
The draft creation cost is relatively lightweight. The workspace is allocated in the repository and
the resources, for updates, are copied.
Comment on lines +575 to +576

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
The draft creation cost is relatively lightweight. The workspace is allocated in the repository and
the resources, for updates, are copied.
The draft creation cost is relatively lightweight. The workspace is allocated in the repository and
the resources are copied in case of updates.


Draft modification costs consist of in-memory operations. During modifications, the repository is not
accessed, but the task handler works.

The most expensive part of the workflow is the draft closure. It contains Git commit and tag creation,
as well as repository write operations.

Rollback costs only appear on error path. This consists of two repository operations, draft closure
and deletion.

## Error Handling

Expand Down
Loading
Loading