33using System . Collections . Generic ;
44using System . Diagnostics ;
55using System . Net ;
6+ using System . Runtime . CompilerServices ;
67using System . Threading . Tasks ;
78using Pipelines . Sockets . Unofficial . Arenas ;
89
@@ -1770,18 +1771,33 @@ public Task ListTrimAsync(RedisKey key, long start, long stop, CommandFlags flag
17701771
17711772 public bool LockExtend ( RedisKey key , RedisValue value , TimeSpan expiry , CommandFlags flags = CommandFlags . None )
17721773 {
1773- if ( value . IsNull ) throw new ArgumentNullException ( nameof ( value ) ) ;
1774- var tran = GetLockExtendTransaction ( key , value , expiry ) ;
1774+ var msg = TryGetLockExtendMessage ( key , value , expiry , flags , out var server ) ;
1775+ if ( msg is not null ) return ExecuteSync ( msg , ResultProcessor . Boolean , server ) ;
17751776
1777+ var tran = GetLockExtendTransaction ( key , value , expiry ) ;
17761778 if ( tran != null ) return tran . Execute ( flags ) ;
17771779
17781780 // without transactions (twemproxy etc), we can't enforce the "value" part
17791781 return KeyExpire ( key , expiry , flags ) ;
17801782 }
17811783
1782- public Task < bool > LockExtendAsync ( RedisKey key , RedisValue value , TimeSpan expiry , CommandFlags flags = CommandFlags . None )
1784+ private Message ? TryGetLockExtendMessage ( in RedisKey key , in RedisValue value , TimeSpan expiry , CommandFlags flags , out ServerEndPoint ? server , [ CallerMemberName ] string ? caller = null )
17831785 {
17841786 if ( value . IsNull ) throw new ArgumentNullException ( nameof ( value ) ) ;
1787+
1788+ // note that lock tokens are expected to be small, so: we'll use IFEQ rather than IFDEQ, for reliability
1789+ // note possible future extension:[P]EXPIRE ... IF* https://github.com/redis/redis/issues/14505
1790+ var features = GetFeatures ( key , flags , RedisCommand . SET , out server ) ;
1791+ return features . SetWithValueCheck
1792+ ? GetStringSetMessage ( key , value , expiry , ValueCondition . Equal ( value ) , flags , caller ) // use check-and-set
1793+ : null ;
1794+ }
1795+
1796+ public Task < bool > LockExtendAsync ( RedisKey key , RedisValue value , TimeSpan expiry , CommandFlags flags = CommandFlags . None )
1797+ {
1798+ var msg = TryGetLockExtendMessage ( key , value , expiry , flags , out var server ) ;
1799+ if ( msg is not null ) return ExecuteAsync ( msg , ResultProcessor . Boolean , server ) ;
1800+
17851801 var tran = GetLockExtendTransaction ( key , value , expiry ) ;
17861802 if ( tran != null ) return tran . ExecuteAsync ( flags ) ;
17871803
@@ -1801,17 +1817,32 @@ public Task<RedisValue> LockQueryAsync(RedisKey key, CommandFlags flags = Comman
18011817
18021818 public bool LockRelease ( RedisKey key , RedisValue value , CommandFlags flags = CommandFlags . None )
18031819 {
1804- if ( value . IsNull ) throw new ArgumentNullException ( nameof ( value ) ) ;
1820+ var msg = TryGetLockReleaseMessage ( key , value , flags , out var server ) ;
1821+ if ( msg is not null ) return ExecuteSync ( msg , ResultProcessor . Boolean , server ) ;
1822+
18051823 var tran = GetLockReleaseTransaction ( key , value ) ;
18061824 if ( tran != null ) return tran . Execute ( flags ) ;
18071825
18081826 // without transactions (twemproxy etc), we can't enforce the "value" part
18091827 return KeyDelete ( key , flags ) ;
18101828 }
18111829
1812- public Task < bool > LockReleaseAsync ( RedisKey key , RedisValue value , CommandFlags flags = CommandFlags . None )
1830+ private Message ? TryGetLockReleaseMessage ( in RedisKey key , in RedisValue value , CommandFlags flags , out ServerEndPoint ? server , [ CallerMemberName ] string ? caller = null )
18131831 {
18141832 if ( value . IsNull ) throw new ArgumentNullException ( nameof ( value ) ) ;
1833+
1834+ // note that lock tokens are expected to be small, so: we'll use IFEQ rather than IFDEQ, for reliability
1835+ var features = GetFeatures ( key , flags , RedisCommand . SET , out server ) ;
1836+ return features . DeleteWithValueCheck
1837+ ? GetStringDeleteMessage ( key , ValueCondition . Equal ( value ) , flags , caller ) // use check-and-delete
1838+ : null ;
1839+ }
1840+
1841+ public Task < bool > LockReleaseAsync ( RedisKey key , RedisValue value , CommandFlags flags = CommandFlags . None )
1842+ {
1843+ var msg = TryGetLockReleaseMessage ( key , value , flags , out var server ) ;
1844+ if ( msg is not null ) return ExecuteAsync ( msg , ResultProcessor . Boolean , server ) ;
1845+
18151846 var tran = GetLockReleaseTransaction ( key , value ) ;
18161847 if ( tran != null ) return tran . ExecuteAsync ( flags ) ;
18171848
0 commit comments