Adjust query frequency based on remaining TTL

After numOfQueriesBeforeBackoff query, the mDNS discovery logic will
enter backoff mode. In backoff mode,  the query frequency will be
updated to max(20, 0.8 * shortest remaining TTL) seconds. It will help
to reduce mDNS query frequency in certain use cases.

Bug: 284480315
Test: atest CtsNetTest FrameworksNetTests
Change-Id: Iac8baaaf58cf9b3b8e67e1cd80402fdecde1d3d4
This commit is contained in:
Yuyang Huang
2023-06-14 14:27:58 +09:00
parent 2388643f92
commit f56c92f3ff
3 changed files with 356 additions and 57 deletions

View File

@@ -288,6 +288,110 @@ public class MdnsServiceTypeClientTests {
verify(expectedSendFutures[5]).cancel(true);
}
@Test
public void sendQueries_activeScanWithQueryBackoff() {
MdnsSearchOptions searchOptions =
MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(
false).setNumOfQueriesBeforeBackoff(11).build();
client.startSendAndReceive(mockListenerOne, searchOptions);
// First burst, 3 queries.
verifyAndSendQuery(0, 0, /* expectsUnicastResponse= */ true);
verifyAndSendQuery(
1, MdnsConfigs.timeBetweenQueriesInBurstMs(), /* expectsUnicastResponse= */ false);
verifyAndSendQuery(
2, MdnsConfigs.timeBetweenQueriesInBurstMs(), /* expectsUnicastResponse= */ false);
// Second burst will be sent after initialTimeBetweenBurstsMs, 3 queries.
verifyAndSendQuery(
3, MdnsConfigs.initialTimeBetweenBurstsMs(), /* expectsUnicastResponse= */ false);
verifyAndSendQuery(
4, MdnsConfigs.timeBetweenQueriesInBurstMs(), /* expectsUnicastResponse= */ false);
verifyAndSendQuery(
5, MdnsConfigs.timeBetweenQueriesInBurstMs(), /* expectsUnicastResponse= */ false);
// Third burst will be sent after initialTimeBetweenBurstsMs * 2, 3 queries.
verifyAndSendQuery(
6, MdnsConfigs.initialTimeBetweenBurstsMs() * 2, /* expectsUnicastResponse= */
false);
verifyAndSendQuery(
7, MdnsConfigs.timeBetweenQueriesInBurstMs(), /* expectsUnicastResponse= */ false);
verifyAndSendQuery(
8, MdnsConfigs.timeBetweenQueriesInBurstMs(), /* expectsUnicastResponse= */ false);
// Forth burst will be sent after initialTimeBetweenBurstsMs * 4, 3 queries.
verifyAndSendQuery(
9, MdnsConfigs.initialTimeBetweenBurstsMs() * 4, /* expectsUnicastResponse= */
false);
verifyAndSendQuery(
10, MdnsConfigs.timeBetweenQueriesInBurstMs(), /* expectsUnicastResponse= */ false);
verifyAndSendQuery(
11, MdnsConfigs.timeBetweenQueriesInBurstMs(), /* expectsUnicastResponse= */ false);
// In backoff mode, the current scheduled task will be canceled and reschedule if the
// 0.8 * smallestRemainingTtl is larger than time to next run.
long currentTime = TEST_TTL / 2 + TEST_ELAPSED_REALTIME;
doReturn(currentTime).when(mockDecoderClock).elapsedRealtime();
client.processResponse(createResponse(
"service-instance-1", "192.0.2.123", 5353,
SERVICE_TYPE_LABELS,
Collections.emptyMap(), TEST_TTL), socketKey);
verifyAndSendQuery(12, (long) (TEST_TTL / 2 * 0.8), /* expectsUnicastResponse= */
false);
currentTime += (long) (TEST_TTL / 2 * 0.8);
doReturn(currentTime).when(mockDecoderClock).elapsedRealtime();
verifyAndSendQuery(
13, MdnsConfigs.timeBetweenQueriesInBurstMs(), /* expectsUnicastResponse= */ false);
}
@Test
public void sendQueries_passiveScanWithQueryBackoff() {
MdnsSearchOptions searchOptions =
MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(
true).setNumOfQueriesBeforeBackoff(3).build();
client.startSendAndReceive(mockListenerOne, searchOptions);
verifyAndSendQuery(0, 0, /* expectsUnicastResponse= */ true);
verifyAndSendQuery(
1, MdnsConfigs.timeBetweenQueriesInBurstMs(), /* expectsUnicastResponse= */ false);
verifyAndSendQuery(
2, MdnsConfigs.timeBetweenQueriesInBurstMs(), /* expectsUnicastResponse= */ false);
verifyAndSendQuery(3, MdnsConfigs.timeBetweenBurstsMs(), /* expectsUnicastResponse= */
false);
assertEquals(4, currentThreadExecutor.getNumOfScheduledFuture());
// In backoff mode, the current scheduled task will be canceled and reschedule if the
// 0.8 * smallestRemainingTtl is larger than time to next run.
doReturn(TEST_ELAPSED_REALTIME + 20000).when(mockDecoderClock).elapsedRealtime();
client.processResponse(createResponse(
"service-instance-1", "192.0.2.123", 5353,
SERVICE_TYPE_LABELS,
Collections.emptyMap(), TEST_TTL), socketKey);
verify(expectedSendFutures[4]).cancel(true);
assertEquals(5, currentThreadExecutor.getNumOfScheduledFuture());
verifyAndSendQuery(4, 80000 /* timeInMs */, false /* expectsUnicastResponse */);
assertEquals(6, currentThreadExecutor.getNumOfScheduledFuture());
// Next run should also be scheduled in 0.8 * smallestRemainingTtl
verifyAndSendQuery(5, 80000 /* timeInMs */, false /* expectsUnicastResponse */);
assertEquals(7, currentThreadExecutor.getNumOfScheduledFuture());
// If the records is not refreshed, the current scheduled task will not be canceled.
doReturn(TEST_ELAPSED_REALTIME + 20001).when(mockDecoderClock).elapsedRealtime();
client.processResponse(createResponse(
"service-instance-1", "192.0.2.123", 5353,
SERVICE_TYPE_LABELS,
Collections.emptyMap(), TEST_TTL,
TEST_ELAPSED_REALTIME - 1), socketKey);
verify(expectedSendFutures[7], never()).cancel(true);
// In backoff mode, the current scheduled task will not be canceled if the
// 0.8 * smallestRemainingTtl is smaller than time to next run.
doReturn(TEST_ELAPSED_REALTIME).when(mockDecoderClock).elapsedRealtime();
client.processResponse(createResponse(
"service-instance-1", "192.0.2.123", 5353,
SERVICE_TYPE_LABELS,
Collections.emptyMap(), TEST_TTL), socketKey);
verify(expectedSendFutures[7], never()).cancel(true);
client.stopSendAndReceive(mockListenerOne);
verify(expectedSendFutures[7]).cancel(true);
}
@Test
public void sendQueries_reentry_passiveScanMode() {
MdnsSearchOptions searchOptions =
@@ -328,7 +432,8 @@ public class MdnsServiceTypeClientTests {
MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(false).build();
QueryTaskConfig config = new QueryTaskConfig(
searchOptions.getSubtypes(), searchOptions.isPassiveMode(),
false /* onlyUseIpv6OnIpv6OnlyNetworks */, 1, socketKey);
false /* onlyUseIpv6OnIpv6OnlyNetworks */, 3 /* numOfQueriesBeforeBackoff */,
socketKey);
// This is the first query. We will ask for unicast response.
assertTrue(config.expectUnicastResponse);
@@ -358,7 +463,8 @@ public class MdnsServiceTypeClientTests {
MdnsSearchOptions.newBuilder().addSubtype("12345").setIsPassiveMode(false).build();
QueryTaskConfig config = new QueryTaskConfig(
searchOptions.getSubtypes(), searchOptions.isPassiveMode(),
false /* onlyUseIpv6OnIpv6OnlyNetworks */, 1, socketKey);
false /* onlyUseIpv6OnIpv6OnlyNetworks */, 3 /* numOfQueriesBeforeBackoff */,
socketKey);
// This is the first query. We will ask for unicast response.
assertTrue(config.expectUnicastResponse);
@@ -714,8 +820,10 @@ public class MdnsServiceTypeClientTests {
return mockPacketWriter;
}
};
MdnsSearchOptions searchOptions = MdnsSearchOptions.newBuilder().setRemoveExpiredService(
true).build();
MdnsSearchOptions searchOptions = MdnsSearchOptions.newBuilder()
.setRemoveExpiredService(true)
.setNumOfQueriesBeforeBackoff(Integer.MAX_VALUE)
.build();
client.startSendAndReceive(mockListenerOne, searchOptions);
Runnable firstMdnsTask = currentThreadExecutor.getAndClearSubmittedRunnable();
@@ -1248,13 +1356,16 @@ public class MdnsServiceTypeClientTests {
final String ipV4Address = "192.0.2.0";
final MdnsSearchOptions resolveOptions = MdnsSearchOptions.newBuilder()
.setNumOfQueriesBeforeBackoff(Integer.MAX_VALUE)
.setResolveInstanceName("instance1").build();
client.startSendAndReceive(mockListenerOne, resolveOptions);
// Ensure the first task is executed so it schedules a future task
currentThreadExecutor.getAndClearSubmittedFuture().get(
TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
client.startSendAndReceive(mockListenerTwo, MdnsSearchOptions.getDefaultOptions());
client.startSendAndReceive(mockListenerTwo,
MdnsSearchOptions.newBuilder().setNumOfQueriesBeforeBackoff(
Integer.MAX_VALUE).build());
// Filing the second request cancels the first future
verify(expectedSendFutures[0]).cancel(true);
@@ -1317,7 +1428,7 @@ public class MdnsServiceTypeClientTests {
private void verifyAndSendQuery(int index, long timeInMs, boolean expectsUnicastResponse,
boolean multipleSocketDiscovery) {
assertEquals(currentThreadExecutor.getAndClearLastScheduledDelayInMs(), timeInMs);
assertEquals(timeInMs, currentThreadExecutor.getAndClearLastScheduledDelayInMs());
currentThreadExecutor.getAndClearLastScheduledRunnable().run();
if (expectsUnicastResponse) {
verify(mockSocketClient).sendPacketRequestingUnicastResponse(
@@ -1406,6 +1517,10 @@ public class MdnsServiceTypeClientTests {
lastSubmittedFuture = null;
return val;
}
public int getNumOfScheduledFuture() {
return futureIndex - 1;
}
}
private MdnsPacket createResponse(
@@ -1424,7 +1539,7 @@ public class MdnsServiceTypeClientTests {
textAttributes, ptrTtlMillis);
}
// Creates a mDNS response.
private MdnsPacket createResponse(
@NonNull String serviceInstanceName,
@Nullable String host,
@@ -1432,6 +1547,19 @@ public class MdnsServiceTypeClientTests {
@NonNull String[] type,
@NonNull Map<String, String> textAttributes,
long ptrTtlMillis) {
return createResponse(serviceInstanceName, host, port, type, textAttributes, ptrTtlMillis,
TEST_ELAPSED_REALTIME);
}
// Creates a mDNS response.
private MdnsPacket createResponse(
@NonNull String serviceInstanceName,
@Nullable String host,
int port,
@NonNull String[] type,
@NonNull Map<String, String> textAttributes,
long ptrTtlMillis,
long receiptTimeMillis) {
final ArrayList<MdnsRecord> answerRecords = new ArrayList<>();
@@ -1442,7 +1570,7 @@ public class MdnsServiceTypeClientTests {
final String[] serviceName = serviceNameList.toArray(new String[0]);
final MdnsPointerRecord pointerRecord = new MdnsPointerRecord(
type,
TEST_ELAPSED_REALTIME /* receiptTimeMillis */,
receiptTimeMillis,
false /* cacheFlush */,
ptrTtlMillis,
serviceName);
@@ -1451,7 +1579,7 @@ public class MdnsServiceTypeClientTests {
// Set SRV record.
final MdnsServiceRecord serviceRecord = new MdnsServiceRecord(
serviceName,
TEST_ELAPSED_REALTIME /* receiptTimeMillis */,
receiptTimeMillis,
false /* cacheFlush */,
TEST_TTL,
0 /* servicePriority */,
@@ -1465,7 +1593,7 @@ public class MdnsServiceTypeClientTests {
final InetAddress addr = InetAddresses.parseNumericAddress(host);
final MdnsInetAddressRecord inetAddressRecord = new MdnsInetAddressRecord(
new String[] {"hostname"} /* name */,
TEST_ELAPSED_REALTIME /* receiptTimeMillis */,
receiptTimeMillis,
false /* cacheFlush */,
TEST_TTL,
addr);
@@ -1479,7 +1607,7 @@ public class MdnsServiceTypeClientTests {
}
final MdnsTextRecord textRecord = new MdnsTextRecord(
serviceName,
TEST_ELAPSED_REALTIME /* receiptTimeMillis */,
receiptTimeMillis,
false /* cacheFlush */,
TEST_TTL,
textEntries);