Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3/3] Spec-compliant CASE eviction policy algorithm #19903

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 84 additions & 5 deletions src/controller/python/test/test_scripts/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ async def TestCaseEviction(self, nodeid: int):
# of eviction, just that allocation and CASE session establishment proceeds successfully on both
# the controller and target.
#
for x in range(minimumSupportedFabrics * minimumCASESessionsPerFabric * 3):
for x in range(minimumSupportedFabrics * minimumCASESessionsPerFabric * 2):
self.devCtrl.CloseSession(nodeid)
await self.devCtrl.ReadAttribute(nodeid, [(Clusters.Basic.Attributes.ClusterRevision)])

Expand All @@ -380,15 +380,94 @@ async def TestCaseEviction(self, nodeid: int):
# This tests establishing a subscription on a given CASE session, then mark it defunct (to simulate
# encountering a transport timeout on the session).
mrjerryjohns marked this conversation as resolved.
Show resolved Hide resolved
#
# At the max interval, we should still have a valid subscription.
# Then, we write to an attribute from a *different* fabric and check to ensure we still get a report
# on the sub we established previously. Since it was just marked defunct, it should return back to being
# active and a report should get delivered.
#
sawValueChange = False

def OnValueChange(path: Attribute.TypedAttributePath, transaction: Attribute.SubscriptionTransaction) -> None:
nonlocal sawValueChange
self.logger.info("Saw value change!")
if (path.AttributeType == Clusters.TestCluster.Attributes.Int8u and path.Path.EndpointId == 1):
sawValueChange = True

self.logger.info("Testing CASE defunct logic")

sub = await self.devCtrl.ReadAttribute(nodeid, [(Clusters.TestCluster.Attributes.Int8u)], reportInterval=(0, 1))
sub.SetAttributeUpdateCallback(OnValueChange)

#
# This marks the session defunct.
#
sub = await self.devCtrl.ReadAttribute(nodeid, [(Clusters.Basic.Attributes.ClusterRevision)], reportInterval=(0, 2))
await asyncio.sleep(2)
self.devCtrl.CloseSession(nodeid)
await asyncio.sleep(4)

#
# Now write the attribute from fabric2, give it some time before checking if the report
# was received.
#
await self.devCtrl2.WriteAttribute(nodeid, [(1, Clusters.TestCluster.Attributes.Int8u(4))])
time.sleep(2)

sub.Shutdown()

if sawValueChange is False:
self.logger.error("Didn't see value change in time, likely because sub got terminated due to unexpected session eviction!")
return False

#
# In this test, we're going to setup a subscription on fabric1 through devCtl, then, constantly keep
# evicting sessions on fabric2 (devCtl2) by cycling through closing sessions followed by issuing a Read. This
# should result in evictions on the server on fabric2, but not affect any sessions on fabric1. To test this,
# we're going to setup a subscription to an attribute prior to the cycling reads, and check at the end of the
# test that it's still valid by writing to an attribute from a *different* fabric, and validating that we see
# the change on the established subscription. That proves that the session from fabric1 is still valid and untouched.
#
self.logger.info("Testing fabric-isolated CASE eviction")

sawValueChange = False
sub = await self.devCtrl.ReadAttribute(nodeid, [(Clusters.TestCluster.Attributes.Int8u)], reportInterval=(0, 1))
sub.SetAttributeUpdateCallback(OnValueChange)

for x in range(minimumSupportedFabrics * minimumCASESessionsPerFabric * 2):
self.devCtrl2.CloseSession(nodeid)
await self.devCtrl2.ReadAttribute(nodeid, [(Clusters.Basic.Attributes.ClusterRevision)])

#
# Now write the attribute from fabric2, give it some time before checking if the report
# was received.
#
await self.devCtrl2.WriteAttribute(nodeid, [(1, Clusters.TestCluster.Attributes.Int8u(4))])
time.sleep(2)

sub.Shutdown()

if sawValueChange is False:
self.logger.error("Didn't see value change in time, likely because sub got terminated due to other fabric (fabric1)")
return False

#
# Do the same test again, but reversing the roles of fabric1 and fabric2.
#
self.logger.info("Testing fabric-isolated CASE eviction (reverse)")

sawValueChange = False
sub = await self.devCtrl2.ReadAttribute(nodeid, [(Clusters.TestCluster.Attributes.Int8u)], reportInterval=(0, 1))
sub.SetAttributeUpdateCallback(OnValueChange)

for x in range(minimumSupportedFabrics * minimumCASESessionsPerFabric * 2):
self.devCtrl.CloseSession(nodeid)
await self.devCtrl.ReadAttribute(nodeid, [(Clusters.Basic.Attributes.ClusterRevision)])

await self.devCtrl.WriteAttribute(nodeid, [(1, Clusters.TestCluster.Attributes.Int8u(4))])
time.sleep(2)

sub.Shutdown()

if sawValueChange is False:
self.logger.error("Didn't see value change in time, likely because sub got terminated due to other fabric (fabric2)")
return False

return True

async def TestMultiFabric(self, ip: str, setuppin: int, nodeid: int):
Expand Down
16 changes: 3 additions & 13 deletions src/controller/python/test/test_scripts/mobile-device-test.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,24 +76,14 @@ def ethernet_commissioning(test: BaseTestHelper, discriminator: int, setup_pin:
nodeid=device_nodeid),
"Failed to finish key exchange")

#
# Run this before the MultiFabric test, since it will result in the resultant CASE session
# on fabric2 to be evicted (due to the stressful nature of that test) on the target.
#
# It doesn't actually evict the CASE session for fabric2 on the client, since we prioritize
# defunct sessions for eviction first, which means our CASE session on fabric2 remains preserved
# throughout the stress test. This results in a mis-match later.
#
# TODO: Once we implement fabric-adjusted LRU, we should see if this issue remains (it shouldn't)
#
logger.info("Testing CASE Eviction")
FailIfNot(asyncio.run(test.TestCaseEviction(device_nodeid)), "Failed TestCaseEviction")

ok = asyncio.run(test.TestMultiFabric(ip=address,
setuppin=20202021,
nodeid=1))
FailIfNot(ok, "Failed to commission multi-fabric")

logger.info("Testing CASE Eviction")
FailIfNot(asyncio.run(test.TestCaseEviction(device_nodeid)), "Failed TestCaseEviction")

logger.info("Testing closing sessions")
FailIfNot(test.TestCloseSession(nodeid=device_nodeid), "Failed to close sessions")

Expand Down
2 changes: 2 additions & 0 deletions src/transport/SecureSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,8 @@ class SecureSession : public Session, public ReferenceCounted<SecureSession, Sec
void MoveToState(State targetState);

friend class SecureSessionDeleter;
friend class TestSecureSessionTable;

SecureSessionTable & mTable;
State mState;
const Type mSecureSessionType;
Expand Down
Loading