Make DnsWriter truly atomic

Right now - if there's an error during DnsWriter.publish*, all the publish from
before that error will be committed, while all the publish after that error
will not.

More than that - in some writers partial publishes can be committed, depending
on implementation.

This defines a new contract that publish* are only committed when .commit is
called. That way any error will simply mean no publish is committed.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=165708063
This commit is contained in:
guyben 2017-08-18 08:27:34 -07:00 committed by Ben McIlwain
parent fcb554947c
commit d5ac03aae4
8 changed files with 204 additions and 90 deletions

View file

@ -28,6 +28,7 @@ import static google.registry.testing.DatastoreHelper.persistDeletedHost;
import static google.registry.testing.DatastoreHelper.persistResource;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import com.google.common.base.VerifyException;
@ -97,7 +98,8 @@ public class DnsUpdateWriterTest {
createTld("tld");
when(mockResolver.send(any(Update.class))).thenReturn(messageWithResponseCode(Rcode.NOERROR));
writer = new DnsUpdateWriter(Duration.ZERO, Duration.ZERO, Duration.ZERO, mockResolver, clock);
writer = new DnsUpdateWriter(
"tld", Duration.ZERO, Duration.ZERO, Duration.ZERO, mockResolver, clock);
}
@Test
@ -112,6 +114,7 @@ public class DnsUpdateWriterTest {
persistResource(domain);
writer.publishDomain("example.tld");
writer.commit();
verify(mockResolver).send(updateCaptor.capture());
Update update = updateCaptor.getValue();
@ -121,6 +124,62 @@ public class DnsUpdateWriterTest {
assertThatTotalUpdateSetsIs(update, 2); // The delete and NS sets
}
@Test
public void testPublishAtomic_noCommit() throws Exception {
HostResource host1 = persistActiveHost("ns.example1.tld");
DomainResource domain1 =
persistActiveDomain("example1.tld")
.asBuilder()
.setNameservers(ImmutableSet.of(Key.create(host1)))
.build();
persistResource(domain1);
HostResource host2 = persistActiveHost("ns.example2.tld");
DomainResource domain2 =
persistActiveDomain("example2.tld")
.asBuilder()
.setNameservers(ImmutableSet.of(Key.create(host2)))
.build();
persistResource(domain2);
writer.publishDomain("example1.tld");
writer.publishDomain("example2.tld");
verifyZeroInteractions(mockResolver);
}
@Test
public void testPublishAtomic_oneUpdate() throws Exception {
HostResource host1 = persistActiveHost("ns.example1.tld");
DomainResource domain1 =
persistActiveDomain("example1.tld")
.asBuilder()
.setNameservers(ImmutableSet.of(Key.create(host1)))
.build();
persistResource(domain1);
HostResource host2 = persistActiveHost("ns.example2.tld");
DomainResource domain2 =
persistActiveDomain("example2.tld")
.asBuilder()
.setNameservers(ImmutableSet.of(Key.create(host2)))
.build();
persistResource(domain2);
writer.publishDomain("example1.tld");
writer.publishDomain("example2.tld");
writer.commit();
verify(mockResolver).send(updateCaptor.capture());
Update update = updateCaptor.getValue();
assertThatUpdatedZoneIs(update, "tld.");
assertThatUpdateDeletes(update, "example1.tld.", Type.ANY);
assertThatUpdateDeletes(update, "example2.tld.", Type.ANY);
assertThatUpdateAdds(update, "example1.tld.", Type.NS, "ns.example1.tld.");
assertThatUpdateAdds(update, "example2.tld.", Type.NS, "ns.example2.tld.");
assertThatTotalUpdateSetsIs(update, 4); // The delete and NS sets for each TLD
}
@Test
public void testPublishDomainCreate_publishesDelegationSigner() throws Exception {
DomainResource domain =
@ -134,6 +193,7 @@ public class DnsUpdateWriterTest {
persistResource(domain);
writer.publishDomain("example.tld");
writer.commit();
verify(mockResolver).send(updateCaptor.capture());
Update update = updateCaptor.getValue();
@ -155,6 +215,7 @@ public class DnsUpdateWriterTest {
persistResource(domain);
writer.publishDomain("example.tld");
writer.commit();
verify(mockResolver).send(updateCaptor.capture());
Update update = updateCaptor.getValue();
@ -168,6 +229,7 @@ public class DnsUpdateWriterTest {
persistDeletedDomain("example.tld", clock.nowUtc().minusDays(1));
writer.publishDomain("example.tld");
writer.commit();
verify(mockResolver).send(updateCaptor.capture());
Update update = updateCaptor.getValue();
@ -196,6 +258,7 @@ public class DnsUpdateWriterTest {
.build());
writer.publishHost("ns1.example.tld");
writer.commit();
verify(mockResolver).send(updateCaptor.capture());
Update update = updateCaptor.getValue();
@ -214,6 +277,7 @@ public class DnsUpdateWriterTest {
persistActiveDomain("example.tld");
writer.publishHost("ns1.example.tld");
writer.commit();
verify(mockResolver).send(updateCaptor.capture());
Update update = updateCaptor.getValue();
@ -233,6 +297,7 @@ public class DnsUpdateWriterTest {
.build());
writer.publishHost("ns1.example.tld");
writer.commit();
verify(mockResolver).send(updateCaptor.capture());
Update update = updateCaptor.getValue();
@ -266,6 +331,7 @@ public class DnsUpdateWriterTest {
.build());
writer.publishDomain("example.tld");
writer.commit();
verify(mockResolver).send(updateCaptor.capture());
Update update = updateCaptor.getValue();
@ -300,6 +366,7 @@ public class DnsUpdateWriterTest {
.build());
writer.publishDomain("example.tld");
writer.commit();
verify(mockResolver).send(updateCaptor.capture());
Update update = updateCaptor.getValue();
@ -325,6 +392,7 @@ public class DnsUpdateWriterTest {
thrown.expect(VerifyException.class, "SERVFAIL");
writer.publishDomain("example.tld");
writer.commit();
}
@Test
@ -339,6 +407,7 @@ public class DnsUpdateWriterTest {
thrown.expect(VerifyException.class, "SERVFAIL");
writer.publishHost("ns1.example.tld");
writer.commit();
}
private void assertThatUpdatedZoneIs(Update update, String zoneName) {