@@ -41,6 +41,43 @@ internal static ReadOnlySpan<byte> StripKeySpacePrefix(ReadOnlySpan<byte> span)
4141 int end = subspan . IndexOf ( "__:"u8 ) ;
4242 if ( end >= 0 ) return subspan . Slice ( end + 3 ) ;
4343 }
44+ else if ( span . Length >= 19 && span . StartsWith ( "__subkeyspace@"u8 ) )
45+ {
46+ // Format: __subkeyspace@{db}__:{key}
47+ // We want to extract {key} for routing
48+ var subspan = span . Slice ( 14 ) ; // Skip "__subkeyspace@"
49+ int end = subspan . IndexOf ( "__:"u8 ) ;
50+ if ( end >= 0 ) return subspan . Slice ( end + 3 ) ;
51+ }
52+ else if ( span . Length >= 20 && span . StartsWith ( "__subkeyspaceitem@"u8 ) )
53+ {
54+ // Format: __subkeyspaceitem@{db}__:{key}\n{field}
55+ // We want to extract {key} for routing
56+ var subspan = span . Slice ( 18 ) ; // Skip "__subkeyspaceitem@"
57+ int end = subspan . IndexOf ( "__:"u8 ) ;
58+ if ( end >= 0 )
59+ {
60+ subspan = subspan . Slice ( end + 3 ) ; // Skip "{db}__:"
61+ // Find the newline that separates key from field
62+ int newline = subspan . IndexOf ( ( byte ) '\n ' ) ;
63+ if ( newline >= 0 ) return subspan . Slice ( 0 , newline ) ; // Return just the key
64+ return subspan ; // No newline found, return rest
65+ }
66+ }
67+ else if ( span . Length >= 23 && span . StartsWith ( "__subkeyspaceevent@"u8 ) )
68+ {
69+ // Format: __subkeyspaceevent@{db}__:{event}|{key}
70+ // We want to extract {key} for routing
71+ var subspan = span . Slice ( 19 ) ; // Skip "__subkeyspaceevent@"
72+ int end = subspan . IndexOf ( "__:"u8 ) ;
73+ if ( end >= 0 )
74+ {
75+ subspan = subspan . Slice ( end + 3 ) ; // Skip "{db}__:"
76+ // Find the pipe that separates event from key
77+ int pipe = subspan . IndexOf ( ( byte ) '|' ) ;
78+ if ( pipe >= 0 ) return subspan . Slice ( pipe + 1 ) ; // Return just the key
79+ }
80+ }
4481 return span ;
4582 }
4683
@@ -365,6 +402,14 @@ public static RedisChannel SubKeySpaceItem(in RedisKey key, in RedisKey subkey,
365402 if ( key . IsEmpty ) throw new ArgumentNullException ( nameof ( key ) ) ;
366403 if ( subkey . IsEmpty ) throw new ArgumentNullException ( nameof ( subkey ) ) ;
367404
405+ // Redis does not issue notifications for keys containing \n to avoid ambiguity in SubKeySpaceItem format
406+ // Check by converting to string and looking for \n
407+ var keyString = key . ToString ( ) ;
408+ if ( keyString ? . IndexOf ( '\n ' ) >= 0 )
409+ {
410+ throw new ArgumentException ( "Keys containing newline characters are not supported for SubKeySpaceItem notifications" , nameof ( key ) ) ;
411+ }
412+
368413 var db = AppendDatabase ( stackalloc byte [ DatabaseScratchBufferSize ] , database , RedisChannelOptions . None ) ;
369414
370415 // __subkeyspaceitem@{db}__:{key}\n{subkey}
@@ -405,6 +450,12 @@ public static RedisChannel SubKeySpaceEvent(ReadOnlySpan<byte> type, in RedisKey
405450 if ( type . IsEmpty ) throw new ArgumentNullException ( nameof ( type ) ) ;
406451 if ( key . IsEmpty ) throw new ArgumentNullException ( nameof ( key ) ) ;
407452
453+ // Redis rejects events containing | to avoid ambiguity in SubKeySpaceEvent format
454+ if ( type . IndexOf ( ( byte ) '|' ) >= 0 )
455+ {
456+ throw new ArgumentException ( "Event types containing pipe characters are not supported for SubKeySpaceEvent notifications" , nameof ( type ) ) ;
457+ }
458+
408459 RedisChannelOptions options = RedisChannelOptions . MultiNode ;
409460 if ( database is null ) options |= RedisChannelOptions . Pattern ;
410461 var db = AppendDatabase ( stackalloc byte [ DatabaseScratchBufferSize ] , database , options ) ;
0 commit comments