# Design: Unified ModifyReplicationRTC Refactor

**Date:** 2026-05-19  
**Branch:** `feat/rtc-replication-billing`  
**Status:** Approved

---

## Background

Phase 1 of the RTC refactor moved enable/disable RTC logic from standalone CRRF DAG steps into the `backup_setting` layer, but introduced two separate action implementations (`EnableReplicationRTCAction` in backup_setting layer, `DisableReplicationRTCAction` in CRRF layer) and a re-entrant special-case path (`isRTCOnlyReentrant`) inside `ModifyRegionSetting`. This design unifies them under a single `BackupSettingModel.ModifyReplicationRTC` interface method and a single unified CRRF action.

## Constraints

- RTC is only supported on AWS (not GCP, not Azure).
- RTC requires dual-region replication to already be active before it can be enabled.
- Disabling dual-region auto-disables RTC at the AWS level. Disabling RTC explicitly (entity update + AWS call) is still performed before CRR teardown for clean state.
- When disabling RTC, the dual-region active check is **not** required (allow disable regardless).
- `ModifyReplicationRTC` is idempotent/re-entrant: calling it when the desired state already matches is a no-op at the AWS level.

---

## Architecture

### 1. New Domain Type

```go
// app/models/domain/backupv2.go
type ModifyReplicationRTCReqV2 struct {
    ClusterID uint64
    Enable    bool
}
```

`EnableRTC bool` is removed from `RegionSettingModificationReqV2`.

---

### 2. BackupSettingModel Interface

New method:

```go
// app/models/backup/interface.go
ModifyReplicationRTC(ctx context.Context, req *domain.ModifyReplicationRTCReqV2) bizerr.BizError
```

---

### 3. Implementation — `impl/replication_rtc.go`

New file following `region_modification.go` patterns:

1. Load `BackupSetting` by `ClusterID`.
2. Validate: bucket provider must be AWS — return error otherwise.
3. When `Enable == true`: validate `Bucket.DualRegionEnable == true` — return error if dual-region is not active.
4. Update `Bucket.RTCEnabled = req.Enable` in DB.
5. Dispatch `DagModifyReplicationRTC`.

---

### 4. backup_setting Layer Action — `ModifyReplicationRTCAction`

**File:** `app/workflow/action/backup_setting/modify_replication_rtc.go`  
**Constant:** `ActionModifyReplicationRTC`

- Replaces and renames `EnableReplicationRTCAction` (old file `enable_dual_region/enable_replication_rtc.go` deleted).
- Reads desired state from entity (`BackupSetting.Bucket.RTCEnabled`), not from task params.
- Calls `AWSBucketManagerModel.ModifyReplicationRTC(enable)` accordingly.
- Idempotent: if AWS state already matches desired state, no-op.

`DagModifyReplicationRTC` continues to use this action (constant name updated).

---

### 5. Unified CRRF Action — `ActionCRRFModifyReplicationRTC`

**File:** `app/workflow/action/crrf/modify_replication_rtc.go`  
**Constant:** `ActionCRRFModifyReplicationRTC = "crrf_modify_replication_rtc"`

- Uses `action.Wrapper` pattern (same as other CRRF actions).
- Reads `"enable"` param from task params (`"true"` / `"false"`).
- Calls `backupSettingModel.ModifyReplicationRTC(ctx, &domain.ModifyReplicationRTCReqV2{ClusterID: ..., Enable: enable})`.
- Both CRRF enable and disable DAGs register this same action name with different `enable` param values.

Replaces both the old `EnableReplicationRTCAction` (CRRF usage) and `DisableReplicationRTCAction`.

---

### 6. CRRF Enable DAG

**File:** `app/workflow/dag/crrf/enable.go`

```
enable_dual_region
    → modify_replication_rtc(enable=true)
        → enable_pitr
            → enable_crrf_daemon
                → toggle_periodic_backup
                    → mark_enabled
```

The `enable_dual_region` CRRF action (`app/workflow/action/crrf/enable/enable_dual_region.go`) is simplified to pure CRR setup: `EnableRTC: true` removed from the `ModifyRegionSetting` call.

---

### 7. CRRF Disable DAG

**File:** `app/workflow/dag/crrf/disable.go`

```
toggle_periodic_backup
  ├── disable_crrf_daemon
  └── modify_replication_rtc(enable=false)   [parallel]
        └── mark_disabled
```

Replaces `disable_replication_rtc` task (old `DisableReplicationRTCAction`) with the unified action.

---

### 8. Cleanup

| Item | Action |
|------|--------|
| `isRTCOnlyReentrant` helper in `region_modification.go` | Remove |
| Re-entrant RTC path in `ModifyRegionSetting` | Remove |
| `EnableRTC bool` in `RegionSettingModificationReqV2` | Remove |
| `EnableRTC` handling in `ModifyRegionSetting` impl | Remove |
| `app/workflow/action/backup_setting/enable_dual_region/enable_replication_rtc.go` | Delete (replaced by `modify_replication_rtc.go`) |
| `app/workflow/action/crrf/disable/disable_replication_rtc.go` | Delete (replaced by unified action) |
| `ActionEnableReplicationRTC` constant | Remove, replace with `ActionModifyReplicationRTC` |
| `ActionCRRFDisableReplicationRTC` constant | Remove, replace with `ActionCRRFModifyReplicationRTC` |

---

### 9. Mock Regeneration

After all changes: run `make generate4test` to regenerate `BackupSettingModel` mock with the new `ModifyReplicationRTC` method.

---

## Files Affected

| File | Change |
|------|--------|
| `app/models/domain/backupv2.go` | Add `ModifyReplicationRTCReqV2`; remove `EnableRTC` from `RegionSettingModificationReqV2` |
| `app/models/backup/interface.go` | Add `ModifyReplicationRTC` method |
| `app/models/backup/impl/replication_rtc.go` | New file: `ModifyReplicationRTC` implementation |
| `app/models/backup/impl/region_modification.go` | Remove `isRTCOnlyReentrant`, re-entrant RTC path, `EnableRTC` handling |
| `app/workflow/constants.go` | Add `ActionModifyReplicationRTC`, `ActionCRRFModifyReplicationRTC`; remove `ActionEnableReplicationRTC`, `ActionCRRFDisableReplicationRTC` |
| `app/workflow/action/backup_setting/modify_replication_rtc.go` | New file: `ModifyReplicationRTCAction` |
| `app/workflow/action/backup_setting/enable_dual_region/enable_replication_rtc.go` | Delete |
| `app/workflow/action/crrf/modify_replication_rtc.go` | New file: unified CRRF action |
| `app/workflow/action/crrf/disable/disable_replication_rtc.go` | Delete |
| `app/workflow/action/crrf/enable/enable_dual_region.go` | Remove `EnableRTC: true` from `ModifyRegionSetting` call |
| `app/workflow/dag/crrf/enable.go` | Insert `modify_replication_rtc` task after `enable_dual_region` |
| `app/workflow/dag/crrf/disable.go` | Swap `disable_replication_rtc` for `modify_replication_rtc` (same DAG shape) |
| `app/workflow/dag/backup_setting/aws/modify_replication_rtc.go` | Update action constant reference |
| `app/workflow/dag/register.go` | Register new action |
| `app/models/backup/mock/` | Regenerated |

### 10. Hook Cleanup in `region_modification.go`

**`awsEnableDualRegionHook.updateEntity`:**  
Remove `RTCEnabled: req.EnableRTC` from the `BucketSetting` initializer — after removing `EnableRTC` from `RegionSettingModificationReqV2`, the field defaults to `false`, which is the correct initial state for a freshly-enabled dual-region.

**`awsDisableDualRegionHook.updateEntity`:**  
Retain `RTCEnabled: false` — disabling dual-region implies RTC is also off, so the entity state should reflect this as a consistency guarantee (regardless of the separate `modify_replication_rtc` CRRF step).
