@@ -1576,6 +1576,160 @@ public async Task ReadItemAsync_WithPPAFEnabledAndSingleMasterAccountWithRespons
15761576 }
15771577 }
15781578
1579+ [ TestMethod ]
1580+ [ Owner ( "ntripician" ) ]
1581+ [ TestCategory ( "MultiRegion" ) ]
1582+ [ Timeout ( 70000 ) ]
1583+ public async Task ReadItemAsync_WithPPAFDynamicOverride ( )
1584+ {
1585+ // Arrange.
1586+ // Enabling fault injection rule to simulate a 503 service unavailable scenario.
1587+ string serviceUnavailableRuleId = "503-rule-" + Guid . NewGuid ( ) . ToString ( ) ;
1588+ FaultInjectionRule serviceUnavailableRule = new FaultInjectionRuleBuilder (
1589+ id : serviceUnavailableRuleId ,
1590+ condition :
1591+ new FaultInjectionConditionBuilder ( )
1592+ . WithOperationType ( FaultInjectionOperationType . ReadItem )
1593+ . WithRegion ( region1 )
1594+ . Build ( ) ,
1595+ result :
1596+ FaultInjectionResultBuilder . GetResultBuilder ( FaultInjectionServerErrorType . ResponseDelay )
1597+ . WithDelay ( TimeSpan . FromMilliseconds ( 3000 ) )
1598+ . Build ( ) )
1599+ . Build ( ) ;
1600+
1601+ List < FaultInjectionRule > rules = new List < FaultInjectionRule > { serviceUnavailableRule } ;
1602+ FaultInjector faultInjector = new FaultInjector ( rules ) ;
1603+
1604+ bool enablePPAF = true ;
1605+
1606+ // Now that the ppaf enablement flag is returned from gateway, we need to intercept the response and remove the flag from the response, so that
1607+ // the environment variable set above is honored.
1608+ HttpClientHandlerHelper httpClientHandlerHelper = new HttpClientHandlerHelper ( )
1609+ {
1610+ ResponseIntercepter = async ( response , request ) =>
1611+ {
1612+ string json = await response ? . Content ? . ReadAsStringAsync ( ) ;
1613+ if ( json . Length > 0 && json . Contains ( "enablePerPartitionFailoverBehavior" ) )
1614+ {
1615+ if ( enablePPAF )
1616+ {
1617+ JObject parsedDatabaseAccountResponse = JObject . Parse ( json ) ;
1618+ parsedDatabaseAccountResponse . Property ( "enablePerPartitionFailoverBehavior" ) . Value = true . ToString ( ) ;
1619+
1620+ HttpResponseMessage interceptedResponse = new ( )
1621+ {
1622+ StatusCode = response . StatusCode ,
1623+ Content = new StringContent ( parsedDatabaseAccountResponse . ToString ( ) ) ,
1624+ Version = response . Version ,
1625+ ReasonPhrase = response . ReasonPhrase ,
1626+ RequestMessage = response . RequestMessage ,
1627+ } ;
1628+
1629+ return interceptedResponse ;
1630+ }
1631+ else
1632+ {
1633+ JObject parsedDatabaseAccountResponse = JObject . Parse ( json ) ;
1634+ parsedDatabaseAccountResponse . Property ( "enablePerPartitionFailoverBehavior" ) . Value = false . ToString ( ) ;
1635+
1636+ HttpResponseMessage interceptedResponse = new ( )
1637+ {
1638+ StatusCode = response . StatusCode ,
1639+ Content = new StringContent ( parsedDatabaseAccountResponse . ToString ( ) ) ,
1640+ Version = response . Version ,
1641+ ReasonPhrase = response . ReasonPhrase ,
1642+ RequestMessage = response . RequestMessage ,
1643+ } ;
1644+
1645+ return interceptedResponse ;
1646+ }
1647+
1648+ }
1649+
1650+ return response ;
1651+ } ,
1652+ } ;
1653+
1654+ List < string > preferredRegions = new List < string > { region1 , region2 , region3 } ;
1655+ CosmosClientOptions cosmosClientOptions = new CosmosClientOptions ( )
1656+ {
1657+ ConsistencyLevel = ConsistencyLevel . Session ,
1658+ FaultInjector = faultInjector ,
1659+ RequestTimeout = TimeSpan . FromSeconds ( 5 ) ,
1660+ ApplicationPreferredRegions = preferredRegions ,
1661+ HttpClientFactory = ( ) => new HttpClient ( httpClientHandlerHelper ) ,
1662+ } ;
1663+
1664+ List < CosmosIntegrationTestObject > itemsList = new ( )
1665+ {
1666+ new ( ) { Id = "smTestId1" , Pk = "smpk1" } ,
1667+ } ;
1668+
1669+ try
1670+ {
1671+ using CosmosClient cosmosClient = new ( connectionString : this . connectionString , clientOptions : cosmosClientOptions ) ;
1672+ Database database = cosmosClient . GetDatabase ( MultiRegionSetupHelpers . dbName ) ;
1673+ Container container = database . GetContainer ( MultiRegionSetupHelpers . containerName ) ;
1674+
1675+ // Act and Assert.
1676+ await this . TryCreateItems ( itemsList ) ;
1677+
1678+ //Must Ensure the data is replicated to all regions
1679+ await Task . Delay ( 3000 ) ;
1680+
1681+ ItemResponse < CosmosIntegrationTestObject > readResponse = await container . ReadItemAsync < CosmosIntegrationTestObject > (
1682+ id : itemsList [ 0 ] . Id ,
1683+ partitionKey : new PartitionKey ( itemsList [ 0 ] . Pk ) ) ;
1684+
1685+ IReadOnlyList < ( string regionName , Uri uri ) > contactedRegionMapping = readResponse . Diagnostics . GetContactedRegions ( ) ;
1686+ HashSet < string > contactedRegions = new ( contactedRegionMapping . Select ( r => r . regionName ) ) ;
1687+
1688+ Assert . AreEqual (
1689+ expected : HttpStatusCode . OK ,
1690+ actual : readResponse . StatusCode ) ;
1691+
1692+ CosmosTraceDiagnostics traceDiagnostic = readResponse . Diagnostics as CosmosTraceDiagnostics ;
1693+ Assert . IsNotNull ( traceDiagnostic ) ;
1694+
1695+ traceDiagnostic . Value . Data . TryGetValue ( "Hedge Context" , out object hedgeContext ) ;
1696+
1697+ Assert . IsNotNull ( hedgeContext ) ;
1698+ List < string > hedgedRegions = ( ( IEnumerable < string > ) hedgeContext ) . ToList ( ) ;
1699+
1700+ Assert . IsTrue ( hedgedRegions . Count > 1 , "Since the first region is not available, the request should atleast hedge to the next region." ) ;
1701+ Assert . IsTrue ( hedgedRegions . Contains ( region1 ) && ( hedgedRegions . Contains ( region2 ) || hedgedRegions . Contains ( region3 ) ) ) ;
1702+
1703+ enablePPAF = false ;
1704+
1705+ //force database account refresh
1706+ await cosmosClient . DocumentClient . GlobalEndpointManager . RefreshLocationAsync ( true ) ;
1707+
1708+ readResponse = await container . ReadItemAsync < CosmosIntegrationTestObject > (
1709+ id : itemsList [ 0 ] . Id ,
1710+ partitionKey : new PartitionKey ( itemsList [ 0 ] . Pk ) ) ;
1711+
1712+ contactedRegionMapping = readResponse . Diagnostics . GetContactedRegions ( ) ;
1713+ contactedRegions = new ( contactedRegionMapping . Select ( r => r . regionName ) ) ;
1714+
1715+ Assert . AreEqual (
1716+ expected : HttpStatusCode . OK ,
1717+ actual : readResponse . StatusCode ) ;
1718+
1719+ traceDiagnostic = readResponse . Diagnostics as CosmosTraceDiagnostics ;
1720+ Assert . IsNotNull ( traceDiagnostic ) ;
1721+
1722+ traceDiagnostic . Value . Data . TryGetValue ( "Hedge Context" , out object hedgeContextNoPPAF ) ;
1723+
1724+ Assert . IsNull ( hedgeContextNoPPAF ) ;
1725+ Assert . IsNull ( cosmosClient . DocumentClient . ConnectionPolicy . AvailabilityStrategy ) ;
1726+ }
1727+ finally
1728+ {
1729+ await this . TryDeleteItems ( itemsList ) ;
1730+ }
1731+ }
1732+
15791733 [ TestMethod ]
15801734 [ Owner ( "nalutripician" ) ]
15811735 [ TestCategory ( "MultiRegion" ) ]
0 commit comments