Skip to content

Commit b538e55

Browse files
author
Yingjian Wu
committed
rename with 2 phase commit idea
1 parent 93b2c57 commit b538e55

File tree

5 files changed

+364
-82
lines changed

5 files changed

+364
-82
lines changed

metacat-common-server/src/main/java/com/netflix/metacat/common/server/usermetadata/ParentChildRelMetadataService.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import com.netflix.metacat.common.server.model.ChildInfo;
66
import com.netflix.metacat.common.server.model.ParentInfo;
77
import com.netflix.metacat.common.server.properties.ParentChildRelationshipProperties;
8+
import org.apache.commons.lang3.tuple.Pair;
9+
import java.util.Optional;
810

911
import java.util.Set;
1012

@@ -59,13 +61,16 @@ void deleteParentChildRelation(
5961
/**
6062
* Renames `oldName` to `newName` in the parentChildRelationship store.
6163
* This involves two steps:
62-
* 1. Rename all records where the child is `oldName` to `newName`
63-
* 2. Rename all records where the parent is `oldName` to `newName`
64+
* 1. duplicate all records where the child is `oldName` with childName = `newName`
65+
* 2. duplicate all records where the parent is `oldName` with parentName `newName`
6466
*
6567
* @param oldName the current name to be renamed
6668
* @param newName the new name to rename to
69+
* @return return a pair of set,
70+
* where the first set represents the affected parent_uuid with parent = oldName
71+
* and the second set represents the affected child_uuid with child = oldName
6772
*/
68-
void rename(
73+
Pair<Set<String>, Set<String>> renameSoftInsert(
6974
QualifiedName oldName,
7075
QualifiedName newName
7176
);
@@ -75,10 +80,13 @@ void rename(
7580
* This involves two steps:
7681
* 1. drop all records where the child column = `name`
7782
* 2. drop all records where the parent column = `name`
83+
* * Note if uuids are specified, it is going to drop the table with that name with the corresponding uuids
7884
* @param name the name of the entity to drop
85+
* @param uuids the uuids to drop, where the first pair is the parent uuid and second pair is the child uuid
7986
*/
8087
void drop(
81-
QualifiedName name
88+
QualifiedName name,
89+
Optional<Pair<Set<String>, Set<String>>> uuids
8290
);
8391

8492
/**

metacat-functional-tests/src/functionalTest/groovy/com/netflix/metacat/ParentChildRelMetadataServiceSpec.groovy

Lines changed: 193 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,13 @@ class ParentChildRelMetadataServiceSpec extends Specification{
229229
service.createParentChildRelation(parent, parentUUID, child, childUUID, type, props)
230230

231231
when:
232-
service.rename(parent, newParent)
232+
def pair = service.renameSoftInsert(parent, newParent)
233+
service.drop(parent, Optional.of(pair))
233234

234235
then:
236+
assert pair.getLeft().size() == 1
237+
assert pair.getRight().isEmpty()
238+
235239
// Test Old Parent Name
236240
assert service.getParents(parent).isEmpty()
237241
assert service.getChildren(parent).isEmpty()
@@ -254,10 +258,13 @@ class ParentChildRelMetadataServiceSpec extends Specification{
254258

255259
// rename back
256260
when:
257-
service.rename(newParent, parent)
261+
pair = service.renameSoftInsert(newParent, parent)
262+
service.drop(newParent, Optional.of(pair))
258263
child_parent_expected = [new ParentInfo(parent.toString(), type, parentUUID)] as Set
259264

260265
then:
266+
assert pair.getLeft().size() == 1
267+
assert pair.getRight().isEmpty()
261268
// Test new Parent Name
262269
assert service.getParents(newParent).isEmpty()
263270
assert service.getChildren(newParent).isEmpty()
@@ -290,28 +297,29 @@ class ParentChildRelMetadataServiceSpec extends Specification{
290297
service.createParentChildRelation(parent, parentUUID, child2, child2UUID, type, props)
291298
def newParent = QualifiedName.ofTable(catalog, database, "np")
292299
def child_parent_expected = [new ParentInfo(newParent.toString(), type, parentUUID)] as Set
300+
def newParent_children_expected = [
301+
new ChildInfo(child1.toString(), type, child1UUID),
302+
new ChildInfo(child2.toString(), type, child2UUID)
303+
] as Set
293304

294305
when:
295-
service.rename(parent, newParent)
306+
def pair = service.renameSoftInsert(parent, newParent)
307+
service.drop(parent, Optional.of(pair))
296308

297309
then:
298310
// Test Old Parent Name
311+
assert pair.getLeft().size() == 1
312+
assert pair.getRight().isEmpty()
299313
assert service.getParents(parent).isEmpty()
300314
assert service.getChildren(parent).isEmpty()
301315
assert !service.isChildTable(parent)
302316
assert !service.isParentTable(parent)
303317

304318
// Test New Parent Name
305319
assert service.getParents(newParent).isEmpty()
306-
def newParent_children_expected = [
307-
new ChildInfo(child1.toString(), type, child1UUID),
308-
new ChildInfo(child2.toString(), type, child2UUID),
309-
] as Set
310320
assert service.getChildren(newParent) == newParent_children_expected
311321
assert !service.isChildTable(newParent)
312322
assert service.isParentTable(newParent)
313-
314-
then:
315323
// Test Child1
316324
assert service.getParents(child1) == child_parent_expected
317325
assert service.isChildTable(child1)
@@ -333,8 +341,12 @@ class ParentChildRelMetadataServiceSpec extends Specification{
333341
def newChild = QualifiedName.ofTable(catalog, database, "nc")
334342

335343
when:
336-
service.rename(child, newChild)
344+
def pair = service.renameSoftInsert(child, newChild)
345+
service.drop(child, Optional.of(pair))
346+
337347
then:
348+
assert pair.getLeft().isEmpty()
349+
assert pair.getRight().size() == 1
338350
// Test Parent
339351
assert service.getParents(parent).isEmpty()
340352
def parent_children_expected = [new ChildInfo(newChild.toString(), type, childUUID)] as Set
@@ -357,10 +369,13 @@ class ParentChildRelMetadataServiceSpec extends Specification{
357369

358370
// rename back
359371
when:
360-
service.rename(newChild, child)
372+
pair = service.renameSoftInsert(newChild, child)
373+
service.drop(newChild, Optional.of(pair))
361374
parent_children_expected = [new ChildInfo(child.toString(), type, childUUID)] as Set
362375

363376
then:
377+
assert pair.getLeft().isEmpty()
378+
assert pair.getRight().size() == 1
364379
// Test Parent
365380
assert service.getParents(parent).isEmpty()
366381
assert parent_children_expected == service.getChildren(parent)
@@ -390,7 +405,7 @@ class ParentChildRelMetadataServiceSpec extends Specification{
390405
def type = "clone";
391406
service.createParentChildRelation(parent, parentUUID, child, childUUID, type, props)
392407
when:
393-
service.drop(child)
408+
service.drop(child, Optional.empty())
394409

395410
then:
396411
// Test Parent
@@ -417,8 +432,9 @@ class ParentChildRelMetadataServiceSpec extends Specification{
417432
service.createParentChildRelation(parent, parentUUID, child, childUUID, type, props)
418433

419434
when:
420-
service.rename(child, newChild)
421-
service.drop(newChild)
435+
def pair = service.renameSoftInsert(child, newChild)
436+
service.drop(child, Optional.of(pair))
437+
service.drop(newChild, Optional.empty())
422438

423439
then:
424440
// Test Parent
@@ -460,14 +476,14 @@ class ParentChildRelMetadataServiceSpec extends Specification{
460476

461477
// rename to an existing parent
462478
when:
463-
service.rename(parent1, parent2)
479+
service.renameSoftInsert(parent1, parent2)
464480
then:
465481
def e = thrown(Exception)
466482
assert e.message.contains("is already a parent table")
467483

468484
// rename to an existing child
469485
when:
470-
service.rename(child2, child1)
486+
service.renameSoftInsert(child2, child1)
471487
then:
472488
e = thrown(Exception)
473489
assert e.message.contains("is already a child table")
@@ -618,4 +634,165 @@ class ParentChildRelMetadataServiceSpec extends Specification{
618634
1 | "CLONE,5" | "CLONE,test,3;OTHER,other,2"| "CLONE,testhive/test/parent,2" | 2
619635
1 | "CLONE,5;Other,3" | "CLONE,test,3;CLONE,other,2"| "CLONE,testhive/test/parent,2;CLONE,testhive/test/other,2" | 2
620636
}
637+
def "Test Parent Rename failed and remove newName"() {
638+
setup:
639+
def parent = QualifiedName.ofTable(catalog, database, "p")
640+
def parentUUID = "p_uuid"
641+
def child = QualifiedName.ofTable(catalog, database, "c")
642+
def childUUID = "c_uuid"
643+
def type = "clone";
644+
def newParent = QualifiedName.ofTable(catalog, database, "np")
645+
service.createParentChildRelation(parent, parentUUID, child, childUUID, type, props)
646+
647+
when:
648+
def pair = service.renameSoftInsert(parent, newParent)
649+
service.drop(newParent, Optional.of(pair))
650+
651+
then:
652+
// Test New Parent Name
653+
assert service.getParents(newParent).isEmpty()
654+
assert service.getChildren(newParent).isEmpty()
655+
656+
// Test Old Parent Name
657+
assert service.getParents(parent).isEmpty()
658+
def newParent_children_expected = [new ChildInfo(child.toString(), type, childUUID)] as Set
659+
assert service.getChildren(parent) == newParent_children_expected
660+
661+
// Test Child
662+
def child_parent_expected = [new ParentInfo(parent.toString(), type, parentUUID)] as Set
663+
assert child_parent_expected == service.getParents(child)
664+
assert service.getChildren(child).isEmpty()
665+
}
666+
667+
def "Test Child Rename failed and remove newName"() {
668+
setup:
669+
def parent = QualifiedName.ofTable(catalog, database, "p")
670+
def parentUUID = "p_uuid"
671+
def child = QualifiedName.ofTable(catalog, database, "c")
672+
def childUUID = "c_uuid"
673+
def type = "clone"
674+
service.createParentChildRelation(parent, parentUUID, child, childUUID, type, props)
675+
def newChild = QualifiedName.ofTable(catalog, database, "nc")
676+
677+
when:
678+
def pair = service.renameSoftInsert(child, newChild)
679+
service.drop(newChild, Optional.of(pair))
680+
then:
681+
// Test Parent
682+
assert service.getParents(parent).isEmpty()
683+
def parent_children_expected = [new ChildInfo(child.toString(), type, childUUID)] as Set
684+
assert parent_children_expected == service.getChildren(parent)
685+
686+
// Test New Child
687+
assert service.getParents(newChild).isEmpty()
688+
assert service.getChildren(newChild).isEmpty()
689+
690+
// Test Child
691+
def child_parent_expected = [new ParentInfo(parent.toString(), type, parentUUID)] as Set
692+
assert child_parent_expected == service.getParents(child)
693+
assert service.getChildren(child).isEmpty()
694+
}
695+
696+
def "Test rename to an existing table in the parent child rel service"() {
697+
given:
698+
def parent = QualifiedName.ofTable(catalog, database, "p")
699+
def parentUUID = "p_uuid"
700+
def child = QualifiedName.ofTable(catalog, database, "c")
701+
def childUUID = "c_uuid"
702+
def type = "clone"
703+
service.createParentChildRelation(parent, parentUUID, child, childUUID, type, props)
704+
def newParentQualifiedName = QualifiedName.ofTable(catalog, database, "np_name")
705+
def newChildQualifiedName = QualifiedName.ofTable(catalog, database, "nc_name")
706+
insertNewParentChildRecord(newParentQualifiedName.toString(), "np_uuid", newChildQualifiedName.toString(), "nc_uuid", "random")
707+
708+
when:
709+
service.renameSoftInsert(parent, newParentQualifiedName)
710+
then:
711+
def e = thrown(RuntimeException)
712+
assert e.message.contains("is already a parent table")
713+
714+
when:
715+
service.renameSoftInsert(child, newChildQualifiedName)
716+
717+
then:
718+
e = thrown(RuntimeException)
719+
assert e.message.contains("is already a child table")
720+
}
721+
722+
// Add a test where between rename and drop old/new name with the same name but different uuid is added
723+
// only those get from rename should be drop
724+
// This case is not possible in reality but wanted to add a test to prove this
725+
def "Simulate Rename child: drop only impacted uuids"() {
726+
setup:
727+
def parent = QualifiedName.ofTable(catalog, database, "p")
728+
def parentUUID = "p_uuid"
729+
def child = QualifiedName.ofTable(catalog, database, "c")
730+
def childUUID = "c_uuid"
731+
def type = "clone"
732+
service.createParentChildRelation(parent, parentUUID, child, childUUID, type, props)
733+
def newChild = QualifiedName.ofTable(catalog, database, "nc")
734+
735+
when:
736+
def pair = service.renameSoftInsert(child, newChild)
737+
def child_parent_expected = [new ParentInfo(parent.toString(), type, parentUUID)] as Set
738+
// Add a record with the same child name but different uuid and since this record is added after rename
739+
// this record should not be dropped during the service.drop
740+
insertNewParentChildRecord(parent.toString(), parentUUID, child.toString(), "c_uuid2", type)
741+
service.drop(child, Optional.of(pair))
742+
then:
743+
// Test Parent
744+
assert service.getParents(parent).isEmpty()
745+
def parent_children_expected = [new ChildInfo(newChild.toString(), type, childUUID),
746+
new ChildInfo(child.toString(), type, "c_uuid2")] as Set
747+
assert parent_children_expected == service.getChildren(parent)
748+
749+
// Test new child
750+
assert service.getParents(newChild) == child_parent_expected
751+
assert service.getChildren(newChild).isEmpty()
752+
753+
// Test old child
754+
assert service.getParents(child) == child_parent_expected
755+
assert service.getChildren(child).isEmpty()
756+
}
757+
758+
// Add a test where between rename and drop old/new name same name but different uuid is added
759+
// only those get from rename should be drop
760+
// This case is not possible in reality but wanted to add a test to prove this
761+
def "Simulate Rename Parent: drop only impacted uuids"() {
762+
setup:
763+
def parent = QualifiedName.ofTable(catalog, database, "p")
764+
def parentUUID = "p_uuid"
765+
def child = QualifiedName.ofTable(catalog, database, "c")
766+
def childUUID = "c_uuid"
767+
def type = "clone"
768+
service.createParentChildRelation(parent, parentUUID, child, childUUID, type, props)
769+
def newParent = QualifiedName.ofTable(catalog, database, "np")
770+
771+
when:
772+
def pair = service.renameSoftInsert(parent, newParent)
773+
// Add a record with the same parent name but different uuid and since this record is added after rename
774+
// this record should not be dropped during the service.drop
775+
insertNewParentChildRecord(parent.toString(), "p_uuid2", child.toString(), "c_uuid1", "clone")
776+
service.drop(parent, Optional.of(pair))
777+
then:
778+
779+
// Test p
780+
assert service.getParents(parent).isEmpty()
781+
def parent_children_expected = [new ChildInfo(child.toString(), "clone", "c_uuid1")] as Set
782+
assert parent_children_expected == service.getChildren(parent)
783+
784+
// Test np
785+
assert service.getParents(newParent).isEmpty()
786+
def new_parent_children_expected = [new ChildInfo(child.toString(), "clone", childUUID)] as Set
787+
assert new_parent_children_expected == service.getChildren(newParent)
788+
789+
// Test child
790+
assert service.getChildren((child)).isEmpty()
791+
def child_parent_expected = [
792+
new ParentInfo(newParent.toString(), "clone", parentUUID),
793+
new ParentInfo(parent.toString(), "clone", "p_uuid2"),
794+
] as Set
795+
assert service.getParents(child) == child_parent_expected
796+
}
797+
621798
}

0 commit comments

Comments
 (0)