Commit 13be0a3
authored
feat(server): add command converting existing application logic functions to prebuilt (#25178)
Follow-up to #25119, which made *new* logic functions of *newly
installed* packaged applications run in PREBUILT mode. That PR
deliberately left already-installed applications on LIVE. This one
backfills them.
## What it does
Adds `upgrade:2-39:convert-logic-functions-to-prebuilt`, a registered
workspace upgrade command. For every provisioned workspace it enables
`IS_LOGIC_FUNCTION_PREBUILT_MODE_ENABLED`, then converts that
workspace's eligible logic functions from LIVE to PREBUILT.
```
upgrade:2-39:convert-logic-functions-to-prebuilt
└─ per workspace enable IS_LOGIC_FUNCTION_PREBUILT_MODE_ENABLED
└─ collect eligible logic functions across every application
└─ per batch of 100, Promise.allSettled
└─ per logic function own transaction + bundle install
```
A logic function is eligible when:
- its application comes from a packaged source (tarball or npm) and is
not deleted
- it is currently LIVE and not deleted
- `isBuildUpToDate` is true and it carries a non-empty checksum
Those last two mirror `isLogicFunctionReadyForPrebuiltInstall`, the
invariant `FlatLogicFunctionValidatorService` enforces on PREBUILT rows.
## Conversion granularity
The command calls
`UpdateLogicFunctionActionHandlerService.executeForMetadata` directly
rather than going through `validateBuildAndRunWorkspaceMigration`. That
is what makes the parallelism possible: the migration path serialises
one build-and-run per application, so the lambda installs ran
application by application. Calling the handler directly lets
`installPrebuiltBundleIfNeeded` run concurrently across logic functions.
Consequences of bypassing the migration path, stated plainly:
- No validator pass. The eligibility predicate mirrors the same
invariant the validator enforces, and the handler re-checks through
`shouldReinstallLogicFunctionPrebuiltBundle`, so the invariant holds,
but it now rests on the predicate.
- No metadata events and no optimistic cache update.
`flatLogicFunctionMaps` is invalidated explicitly once the workspace
finishes. Metadata version is unaffected: it only increments for object
and field metadata.
- The update partial is exactly `{ executionMode: PREBUILT }` rather
than a full entity diffed against the builder's own read, which removes
the stale-row replacement cubic raised on the earlier shape.
## The bundle is installed by the conversion, not on first execution
```
convertLogicFunctionToPrebuilt
└─ queryRunner.startTransaction()
└─ UpdateLogicFunctionActionHandlerService.executeForMetadata
├─ logicFunctionRepository.update (LIVE -> PREBUILT)
└─ installPrebuiltBundleIfNeeded -> driver.installPrebuiltBundle
├─ getBuiltCode (built JS from file storage)
├─ createZipFile
├─ UpdateFunctionCode <- stores it in the lambda
├─ waitFunctionUpdated
└─ TagResource (checksum tag)
└─ commit
```
Nothing is *built* here: eligibility requires `isBuildUpToDate` and a
checksum, so the artifact already exists in file storage and the drivers
only install it. A function with a stale build is skipped rather than
rebuilt.
The on-demand installer from #25119 stays as the safety net for a node
that missed the conversion.
## Failure and concurrency behaviour
- **One transaction per logic function.** The row update and its bundle
install commit together, so a failed install rolls that row back to LIVE
and the next run reconsiders it. Nothing else in the batch is affected.
- **One failure does not abort the rest.** `Promise.allSettled` per
batch; rejections are logged per function and the workspace continues.
- **A conversion failure does not fail the upgrade.** The command
reports a summary and returns. It is idempotent: eligibility is keyed on
`executionMode = LIVE`, and `installPrebuiltBundle` re-checks the
installed checksum inside its lock.
- **Rollback** is instant and needs no data change: with the feature
flag off, `resolveEffectiveExecutionMode` forces LIVE regardless of the
stored mode.
**Open question for review.** A transaction per logic function means one
pooled connection per in-flight conversion, held for the whole install.
`PG_POOL_MAX_CONNECTIONS` defaults to 10 and a lambda update can run to
60s, so a batch of 100 does not run 100 conversions at once — 10 acquire
connections and the rest queue, with the upgrade holding every core
connection meanwhile. Effective concurrency is pool-bound. Worth
deciding whether to lower the batch size, raise the pool for the upgrade
process, or accept it.
## Note on the feature flag
The upgrade enables `IS_LOGIC_FUNCTION_PREBUILT_MODE_ENABLED` on every
provisioned workspace, so it is no longer a brake on the rollout. Gated
on a flag that is off almost everywhere, an upgrade command would be a
no-op for most workspaces. The flag is only written where it is not
already set, since enabling invalidates and recomputes the workspace
cache.
## The workflowVersion guard
`cec8ed74` also fixes
`WorkspaceWorkflowAutomatedTriggerMapCacheService.findWorkspaceVersionIdByCoreVersionId`,
which is a **separate logical change** and is called out as such.
It queried the workspace `workflowVersion` object on
`coreWorkflowVersionId`, a field that does not exist on a database
upgrading from before the workflow-core migration (#25104). The query
threw, and one workspace's failed cache recompute aborted the entire
upgrade sequence — `cross-version-upgrade` reported 70 workspaces
succeeded, 2 failed, everything stopped.
It is guarded the way
`WorkflowVersionCoreSyncService.workspaceHasCoreWorkflowVersionIdField`
already guards this exact field; the map degrades to a null workspace
twin id, which `computeAutomatedTriggerFromWorkflowVersion` already
accepts. It no-ops once an equivalent guard lands on main.
It is bundled here because `cross-version-upgrade` could not go green on
this branch without it. If #25104's authors would rather own it, lifting
those 27 lines into their own PR and dropping the commit here is clean.
## Test plan
- `is-logic-function-eligible-for-prebuilt-conversion.util.spec.ts`: 9
cases covering both packaged sources, both unpackaged sources,
already-prebuilt, stale build, null and empty checksum, soft-deleted
- 95 tests pass across the workflow and logic-function suites, including
main's own `compute-automated-trigger-from-workflow-version` spec which
exercises the degraded path
- `nx build twenty-server`, `tsgo --noEmit`, `oxlint --type-aware` and
`oxfmt --check` all clean
- an earlier revision was tested locally end to end by @martmull
Full CI is green on `cec8ed74`, including `cross-version-upgrade` and
`server-validation`.
Not yet run against production data, and the 100-way concurrent
`UpdateFunctionCode` has not been exercised against real lambda
control-plane limits.
## History
This started as a standalone `logic-function:convert-to-prebuilt`
command enqueuing per-application jobs on `logicFunctionQueue`. After
@Weiko and @prastoin it became an upgrade command with inline
conversion; after @martmull and @prastoin the conversion service was
folded into the command, then re-cut from per-application migrations to
per-logic-function transactions. The queue-related findings from the
earlier shape (job priority, retry policy, `bulkAdd` bounds,
`lockDuration`) no longer apply.1 parent 28f6e37 commit 13be0a3
7 files changed
Lines changed: 414 additions & 0 deletions
File tree
- packages/twenty-server/src
- database/commands/upgrade-version-command
- 2-39
- engine
- core-modules/workflow/services
- metadata-modules/logic-function/utils
- __tests__
- workspace-manager/workspace-migration/workspace-migration-runner/action-handlers
Lines changed: 20 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
Lines changed: 251 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
| 34 | + | |
34 | 35 | | |
35 | 36 | | |
36 | 37 | | |
| |||
75 | 76 | | |
76 | 77 | | |
77 | 78 | | |
| 79 | + | |
78 | 80 | | |
79 | 81 | | |
80 | 82 | | |
Lines changed: 27 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
| |||
14 | 15 | | |
15 | 16 | | |
16 | 17 | | |
| 18 | + | |
17 | 19 | | |
18 | 20 | | |
19 | 21 | | |
| |||
29 | 31 | | |
30 | 32 | | |
31 | 33 | | |
| 34 | + | |
32 | 35 | | |
33 | 36 | | |
34 | 37 | | |
| |||
75 | 78 | | |
76 | 79 | | |
77 | 80 | | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
78 | 89 | | |
79 | 90 | | |
80 | 91 | | |
| |||
143 | 154 | | |
144 | 155 | | |
145 | 156 | | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
146 | 173 | | |
0 commit comments