Skip to content

Commit 2bf1c83

Browse files
committed
Trim verbose comments, keep only essential 'why' explanations
1 parent e08d531 commit 2bf1c83

2 files changed

Lines changed: 17 additions & 102 deletions

File tree

src/Migration/Destinations/Appwrite.php

Lines changed: 9 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -422,21 +422,13 @@ protected function createDatabase(Database $resource): bool
422422
$createdAt = $this->normalizeDateTime($resource->getCreatedAt());
423423
$updatedAt = $this->normalizeDateTime($resource->getUpdatedAt(), $createdAt);
424424

425-
// Skip/Upsert: pre-check an existing database. Databases contain the
426-
// entire tree of collections + rows, so they are never destructively
427-
// reconciled — under Upsert-newer the container metadata is updated
428-
// in place (name, enabled, etc.) but children are untouched.
429-
//
430-
// Fail mode short-circuits: no pre-check, let the create below throw
431-
// DuplicateException as designed. Saves N metadata reads on the
432-
// common first-time-migration path.
425+
// Fail mode skips the pre-check so library's DuplicateException surfaces on re-migration.
433426
if ($this->onDuplicate !== OnDuplicate::Fail) {
434427
$existing = $this->dbForProject->getDocument('databases', $resource->getId());
435428
$action = $this->onDuplicate->resolveSchemaAction(
436429
!$existing->isEmpty(),
437430
$updatedAt,
438431
$existing->getUpdatedAt(),
439-
// canDrop: false by default — databases hold child tables + rows.
440432
);
441433

442434
if ($action === SchemaAction::Tolerate) {
@@ -457,7 +449,6 @@ protected function createDatabase(Database $resource): bool
457449
$resource->setSequence($existing->getSequence());
458450
return true;
459451
}
460-
// SchemaAction::Create → fall through to the normal create flow.
461452
}
462453

463454
$database = $this->dbForProject->createDocument('databases', new UtopiaDocument([
@@ -541,12 +532,6 @@ protected function createEntity(Table $resource): bool
541532
$dbForDatabases->create();
542533
}
543534

544-
// Skip/Upsert: pre-check an existing table. Like databases, tables
545-
// contain user data (rows) so they are never destructively
546-
// reconciled — under Upsert-newer the container metadata is
547-
// updated in place (name, enabled, permissions, etc.) but child
548-
// rows are untouched. Attribute/index reconciliation happens
549-
// per-resource at a lower layer. Fail short-circuits.
550535
if ($this->onDuplicate !== OnDuplicate::Fail) {
551536
$existing = $this->dbForProject->getDocument(
552537
'database_' . $database->getSequence(),
@@ -556,7 +541,6 @@ protected function createEntity(Table $resource): bool
556541
!$existing->isEmpty(),
557542
$updatedAt,
558543
$existing->getUpdatedAt(),
559-
// canDrop: false by default — tables hold child rows.
560544
);
561545

562546
if ($action === SchemaAction::Tolerate) {
@@ -580,7 +564,6 @@ protected function createEntity(Table $resource): bool
580564
$resource->setSequence($existing->getSequence());
581565
return true;
582566
}
583-
// SchemaAction::Create → fall through to the normal create flow.
584567
}
585568

586569
$table = $this->dbForProject->createDocument('database_' . $database->getSequence(), new UtopiaDocument([
@@ -723,17 +706,14 @@ protected function createField(Column|Attribute $resource): bool
723706
$updatedAt = $this->normalizeDateTime($resource->getUpdatedAt(), $createdAt);
724707
$dbForDatabases = ($this->getDatabasesDB)($database);
725708

726-
// Skip/Upsert: pre-check against `attributes` metadata via one
727-
// resolveSchemaAction call. Fail mode short-circuits to preserve
728-
// zero-overhead fresh-migration behavior.
729709
$attributeMetaId = $database->getSequence() . '_' . $table->getSequence() . '_' . $resource->getKey();
730710
if ($this->onDuplicate !== OnDuplicate::Fail) {
731711
$existingAttr = $this->dbForProject->getDocument('attributes', $attributeMetaId);
732712
$action = $this->onDuplicate->resolveSchemaAction(
733713
!$existingAttr->isEmpty(),
734714
$updatedAt,
735715
$existingAttr->getUpdatedAt(),
736-
canDrop: true, // attributes are leaves; column data repopulates via row Upsert
716+
canDrop: true,
737717
);
738718

739719
if ($action === SchemaAction::Tolerate) {
@@ -752,7 +732,6 @@ protected function createField(Column|Attribute $resource): bool
752732
$dbForDatabases,
753733
);
754734
}
755-
// SchemaAction::Create → fall through to the normal create flow.
756735
}
757736

758737
try {
@@ -1033,18 +1012,14 @@ protected function createIndex(Index $resource): bool
10331012
);
10341013
}
10351014

1036-
// Skip/Upsert: pre-check against `indexes` metadata via one
1037-
// resolveSchemaAction call. Same rule as attributes, except that
1038-
// index drops are non-destructive (no row data lives in indexes) so
1039-
// rebuild cost is just the index build time. Fail short-circuits.
10401015
$indexMetaId = $database->getSequence() . '_' . $table->getSequence() . '_' . $resource->getKey();
10411016
if ($this->onDuplicate !== OnDuplicate::Fail) {
10421017
$existingIdx = $this->dbForProject->getDocument('indexes', $indexMetaId);
10431018
$action = $this->onDuplicate->resolveSchemaAction(
10441019
!$existingIdx->isEmpty(),
10451020
$updatedAt,
10461021
$existingIdx->getUpdatedAt(),
1047-
canDrop: true, // indexes are leaves; carry no user data
1022+
canDrop: true,
10481023
);
10491024

10501025
if ($action === SchemaAction::Tolerate) {
@@ -1066,7 +1041,6 @@ protected function createIndex(Index $resource): bool
10661041
$table->getId()
10671042
);
10681043
}
1069-
// SchemaAction::Create → fall through to the normal create flow.
10701044
}
10711045

10721046
$index = $this->dbForProject->createDocument('indexes', $index);
@@ -1244,26 +1218,11 @@ protected function createRecord(Row $resource, bool $isLast): bool
12441218
}
12451219

12461220
/**
1247-
* Fully remove an attribute from destination — both metadata documents
1248-
* and the physical column(s) — so the caller can create it fresh without
1249-
* colliding with any remnants.
1221+
* Drop an attribute (metadata doc + physical column) so it can be recreated.
12501222
*
1251-
* For plain attributes this is a parent-side cleanup. For two-way
1252-
* relationship attributes, createField writes a second `attributes`
1253-
* document on the related table (under {dbSeq}_{relatedTableSeq}_{twoWayKey})
1254-
* in addition to the parent-side document. Dropping only the parent
1255-
* side leaves the child document dangling and a subsequent recreate
1256-
* collides on it with DuplicateException. This method mirrors
1257-
* createField's two-doc write so the "reconcile" path is symmetric.
1258-
*
1259-
* Physical cleanup on the related table is best-effort: the library's
1260-
* deleteAttribute on the parent side can cascade to the child column
1261-
* depending on relationship handling, so the second deleteAttribute may
1262-
* no-op or NotFoundException — both swallowed.
1263-
*
1264-
* @param UtopiaDocument|null $relatedTable null when the attribute isn't
1265-
* a two-way relationship;
1266-
* required otherwise.
1223+
* Two-way relationships require mirroring: createField writes a second
1224+
* `attributes` document on the related table keyed by twoWayKey. Dropping
1225+
* only the parent leaves that child doc dangling and the recreate collides.
12671226
*/
12681227
private function deleteAttributeCompletely(
12691228
UtopiaDocument $database,
@@ -1276,15 +1235,11 @@ private function deleteAttributeCompletely(
12761235
$collectionId = 'database_' . $database->getSequence() . '_collection_' . $table->getSequence();
12771236
$attributeMetaId = $database->getSequence() . '_' . $table->getSequence() . '_' . $resource->getKey();
12781237

1279-
// Parent side.
12801238
$dbForDatabases->deleteAttribute($collectionId, $resource->getKey());
12811239
$this->dbForProject->deleteDocument('attributes', $attributeMetaId);
12821240
$this->dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $table->getId());
12831241
$dbForDatabases->purgeCachedCollection($collectionId);
12841242

1285-
// Child side, only for two-way relationships. createField writes a
1286-
// second `attributes` document keyed on the related table; mirror
1287-
// that write here.
12881243
if ($type !== UtopiaDatabase::VAR_RELATIONSHIP || $relatedTable === null) {
12891244
return;
12901245
}
@@ -1303,13 +1258,12 @@ private function deleteAttributeCompletely(
13031258
try {
13041259
$this->dbForProject->deleteDocument('attributes', $childMetaId);
13051260
} catch (\Throwable) {
1306-
// Child metadata already gone — interrupted prior run.
1261+
// already gone
13071262
}
13081263
try {
13091264
$dbForDatabases->deleteAttribute($childCollectionId, $twoWayKey);
13101265
} catch (\Throwable) {
1311-
// Physical column already gone — parent-side deleteAttribute may
1312-
// have cascaded via utopia-php/database relationship handling.
1266+
// parent-side delete may have cascaded
13131267
}
13141268
$this->dbForProject->purgeCachedDocument('database_' . $database->getSequence(), $relatedTable->getId());
13151269
$dbForDatabases->purgeCachedCollection($childCollectionId);

src/Migration/Destinations/OnDuplicate.php

Lines changed: 8 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,16 @@
33
namespace Utopia\Migration\Destinations;
44

55
/**
6-
* Caller branches on one of these outcomes after asking
7-
* {@see OnDuplicate::resolveSchemaAction()} what to do with a possibly
8-
* pre-existing schema resource on the destination.
6+
* Outcome of {@see OnDuplicate::resolveSchemaAction()}.
97
*/
108
enum SchemaAction
119
{
12-
/** Resource doesn't exist — run the normal create flow. */
1310
case Create;
14-
15-
/** Resource exists; leave it alone (Skip, or Upsert with dest up-to-date). */
1611
case Tolerate;
17-
18-
/**
19-
* Resource exists; Upsert mode + source strictly newer + resource is a
20-
* leaf (attribute/index) — drop + recreate. Data-preserving containers
21-
* get {@see self::UpdateInPlace} instead.
22-
*/
2312
case DropAndRecreate;
24-
25-
/**
26-
* Resource exists; Upsert mode + source strictly newer + resource is a
27-
* container (database/table) — update metadata in place without
28-
* touching children. Callers should only overwrite fields that are
29-
* safe to source-wins (name, enabled, search, permissions, etc.) and
30-
* must never touch immutable fields ($id, $createdAt, internal
31-
* sequences).
32-
*/
3313
case UpdateInPlace;
3414
}
3515

36-
/**
37-
* Behavior when a destination row with an existing ID is encountered.
38-
*/
3916
enum OnDuplicate: string
4017
{
4118
case Fail = 'fail';
@@ -51,22 +28,11 @@ public static function values(): array
5128
}
5229

5330
/**
54-
* Single decision point for schema-level reconciliation on re-migration.
55-
*
56-
* Callers typically short-circuit on Fail mode before invoking this (to
57-
* avoid the destination metadata lookup entirely — the library's own
58-
* DuplicateException surfaces from the create call as designed).
31+
* Schema-level reconciliation decision.
5932
*
60-
* $canDrop picks the Upsert-newer reconciliation strategy; default
61-
* false is the safe option — destructive reconciliation requires
62-
* explicit opt-in at the call site:
63-
* - true → DropAndRecreate (attributes, indexes; column data is
64-
* repopulated by the follow-up row Upsert, or is pure
65-
* metadata for indexes).
66-
* - false → UpdateInPlace (databases, tables; their child rows and
67-
* sub-resources must be preserved, so destructive
68-
* reconciliation is replaced with an updateDocument on the
69-
* container's own metadata document).
33+
* $canDrop = true → leaves (attributes, indexes) get DropAndRecreate on Upsert-newer.
34+
* $canDrop = false → containers (databases, tables) get UpdateInPlace on Upsert-newer.
35+
* Default is false so destructive reconciliation requires explicit opt-in.
7036
*/
7137
public function resolveSchemaAction(
7238
bool $exists,
@@ -78,7 +44,7 @@ public function resolveSchemaAction(
7844
return SchemaAction::Create;
7945
}
8046
return match ($this) {
81-
self::Fail => SchemaAction::Create, // caller's create flow will throw
47+
self::Fail => SchemaAction::Create,
8248
self::Skip => SchemaAction::Tolerate,
8349
self::Upsert => $this->sourceIsNewer($sourceUpdatedAt, $destUpdatedAt)
8450
? ($canDrop ? SchemaAction::DropAndRecreate : SchemaAction::UpdateInPlace)
@@ -87,13 +53,8 @@ public function resolveSchemaAction(
8753
}
8854

8955
/**
90-
* Unparseable or clearly-invalid timestamps → false (conservative:
91-
* preserve the existing destination rather than risk a destructive drop
92-
* on garbage input). PHP's strtotime() accepts some malformed dates
93-
* leniently — '0000-00-00 00:00:00' for example parses to a large
94-
* negative epoch rather than returning false — so we also reject
95-
* non-positive epochs. Any legitimate Appwrite updatedAt is well past
96-
* 1970-01-01.
56+
* strtotime() accepts '0000-00-00' leniently (returns a large negative
57+
* epoch, not false), so reject non-positive epochs too.
9758
*/
9859
private function sourceIsNewer(string $source, string $dest): bool
9960
{

0 commit comments

Comments
 (0)