Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
StackExchange.Redis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
tsai
StackExchange.Redis
Commits
e931d6a7
Commit
e931d6a7
authored
Mar 20, 2014
by
Marc Gravell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Per-server script cache (aka EVALSHA)
parent
0130f2ee
Changes
13
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
419 additions
and
75 deletions
+419
-75
Scripting.cs
StackExchange.Redis.Tests/Scripting.cs
+100
-0
TestBase.cs
StackExchange.Redis.Tests/TestBase.cs
+15
-1
DebuggingAids.cs
StackExchange.Redis/StackExchange/Redis/DebuggingAids.cs
+1
-0
IServer.cs
StackExchange.Redis/StackExchange/Redis/IServer.cs
+40
-0
Message.cs
StackExchange.Redis/StackExchange/Redis/Message.cs
+1
-0
PhysicalBridge.cs
StackExchange.Redis/StackExchange/Redis/PhysicalBridge.cs
+8
-5
PhysicalConnection.cs
...kExchange.Redis/StackExchange/Redis/PhysicalConnection.cs
+5
-3
RedisCommand.cs
StackExchange.Redis/StackExchange/Redis/RedisCommand.cs
+1
-0
RedisDatabase.cs
StackExchange.Redis/StackExchange/Redis/RedisDatabase.cs
+45
-8
RedisLiterals.cs
StackExchange.Redis/StackExchange/Redis/RedisLiterals.cs
+3
-0
RedisServer.cs
StackExchange.Redis/StackExchange/Redis/RedisServer.cs
+134
-55
ResultProcessor.cs
StackExchange.Redis/StackExchange/Redis/ResultProcessor.cs
+45
-3
ServerEndPoint.cs
StackExchange.Redis/StackExchange/Redis/ServerEndPoint.cs
+21
-0
No files found.
StackExchange.Redis.Tests/Scripting.cs
View file @
e931d6a7
using
System
;
using
System
;
using
System.Diagnostics
;
using
NUnit.Framework
;
using
NUnit.Framework
;
namespace
StackExchange.Redis.Tests
namespace
StackExchange.Redis.Tests
...
@@ -27,5 +28,104 @@ public void TestBasicScripting()
...
@@ -27,5 +28,104 @@ public void TestBasicScripting()
Assert
.
IsFalse
(
wasSet
);
Assert
.
IsFalse
(
wasSet
);
}
}
}
}
[
Test
]
public
void
CheckLoads
()
{
using
(
var
conn0
=
Create
(
allowAdmin
:
true
))
using
(
var
conn1
=
Create
(
allowAdmin
:
true
))
{
// note that these are on different connections (so we wouldn't expect
// the flush to drop the local cache - assume it is a surprise!)
var
server
=
conn0
.
GetServer
(
PrimaryServer
,
PrimaryPort
);
var
db
=
conn1
.
GetDatabase
();
const
string
script
=
"return 1;"
;
// start empty
server
.
ScriptFlush
();
Assert
.
IsFalse
(
server
.
ScriptExists
(
script
));
// run once, causes to be cached
Assert
.
IsTrue
((
bool
)
db
.
ScriptEvaluate
(
script
));
Assert
.
IsTrue
(
server
.
ScriptExists
(
script
));
// can run again
Assert
.
IsTrue
((
bool
)
db
.
ScriptEvaluate
(
script
));
// ditch the scripts; should no longer exist
db
.
Ping
();
server
.
ScriptFlush
();
Assert
.
IsFalse
(
server
.
ScriptExists
(
script
));
db
.
Ping
();
// now: fails the first time
try
{
Assert
.
IsTrue
((
bool
)
db
.
ScriptEvaluate
(
script
));
Assert
.
Fail
();
}
catch
(
RedisServerException
ex
)
{
Assert
.
IsTrue
(
ex
.
Message
==
"NOSCRIPT No matching script. Please use EVAL."
);
}
// but gets marked as unloaded, so we can use it again...
Assert
.
IsTrue
((
bool
)
db
.
ScriptEvaluate
(
script
));
// which will cause it to be cached
Assert
.
IsTrue
(
server
.
ScriptExists
(
script
));
}
}
[
Test
]
public
void
CompareScriptToDirect
()
{
const
string
Script
=
"return redis.call('incr', KEYS[1])"
;
using
(
var
conn
=
Create
(
allowAdmin
:
true
))
{
var
server
=
conn
.
GetServer
(
PrimaryServer
,
PrimaryPort
);
server
.
FlushAllDatabases
();
server
.
ScriptFlush
();
server
.
ScriptLoad
(
Script
);
var
db
=
conn
.
GetDatabase
();
db
.
Ping
();
// k, we're all up to date now; clean db, minimal script cache
// we're using a pipeline here, so send 1000 messages, but for timing: only care about the last
const
int
LOOP
=
5000
;
RedisKey
key
=
"foo"
;
RedisKey
[]
keys
=
new
[]
{
key
};
// script takes an array
// run via script
db
.
KeyDelete
(
key
);
CollectGarbage
();
var
watch
=
Stopwatch
.
StartNew
();
for
(
int
i
=
1
;
i
<
LOOP
;
i
++)
// the i=1 is to do all-but-one
{
db
.
ScriptEvaluate
(
Script
,
keys
,
flags
:
CommandFlags
.
FireAndForget
);
}
var
scriptResult
=
db
.
ScriptEvaluate
(
Script
,
keys
);
// last one we wait for (no F+F)
watch
.
Stop
();
TimeSpan
scriptTime
=
watch
.
Elapsed
;
// run via raw op
db
.
KeyDelete
(
key
);
CollectGarbage
();
watch
=
Stopwatch
.
StartNew
();
for
(
int
i
=
1
;
i
<
LOOP
;
i
++)
// the i=1 is to do all-but-one
{
db
.
StringIncrement
(
key
,
flags
:
CommandFlags
.
FireAndForget
);
}
var
directResult
=
db
.
StringIncrement
(
key
);
// last one we wait for (no F+F)
watch
.
Stop
();
TimeSpan
directTime
=
watch
.
Elapsed
;
Assert
.
AreEqual
(
LOOP
,
(
long
)
scriptResult
,
"script result"
);
Assert
.
AreEqual
(
LOOP
,
(
long
)
directResult
,
"direct result"
);
Console
.
WriteLine
(
"script: {0}ms; direct: {1}ms"
,
scriptTime
.
TotalMilliseconds
,
directTime
.
TotalMilliseconds
);
}
}
}
}
}
}
StackExchange.Redis.Tests/TestBase.cs
View file @
e931d6a7
...
@@ -16,6 +16,14 @@ namespace StackExchange.Redis.Tests
...
@@ -16,6 +16,14 @@ namespace StackExchange.Redis.Tests
public
abstract
class
TestBase
:
IDisposable
public
abstract
class
TestBase
:
IDisposable
{
{
protected
void
CollectGarbage
()
{
for
(
int
i
=
0
;
i
<
3
;
i
++)
{
GC
.
Collect
(
GC
.
MaxGeneration
,
GCCollectionMode
.
Forced
);
GC
.
WaitForPendingFinalizers
();
}
}
private
readonly
SocketManager
socketManager
;
private
readonly
SocketManager
socketManager
;
protected
TestBase
()
protected
TestBase
()
...
@@ -138,6 +146,12 @@ protected IServer GetServer(ConnectionMultiplexer muxer)
...
@@ -138,6 +146,12 @@ protected IServer GetServer(ConnectionMultiplexer muxer)
map
[
cmd
]
=
null
;
map
[
cmd
]
=
null
;
config
.
CommandMap
=
CommandMap
.
Create
(
map
);
config
.
CommandMap
=
CommandMap
.
Create
(
map
);
}
}
if
(
Debugger
.
IsAttached
)
{
syncTimeout
=
int
.
MaxValue
;
}
if
(
useSharedSocketManager
)
config
.
SocketManager
=
socketManager
;
if
(
useSharedSocketManager
)
config
.
SocketManager
=
socketManager
;
if
(
channelPrefix
!=
null
)
config
.
ChannelPrefix
=
channelPrefix
;
if
(
channelPrefix
!=
null
)
config
.
ChannelPrefix
=
channelPrefix
;
if
(
tieBreaker
!=
null
)
config
.
TieBreaker
=
tieBreaker
;
if
(
tieBreaker
!=
null
)
config
.
TieBreaker
=
tieBreaker
;
...
@@ -149,7 +163,7 @@ protected IServer GetServer(ConnectionMultiplexer muxer)
...
@@ -149,7 +163,7 @@ protected IServer GetServer(ConnectionMultiplexer muxer)
if
(
connectTimeout
!=
null
)
config
.
ConnectTimeout
=
connectTimeout
.
Value
;
if
(
connectTimeout
!=
null
)
config
.
ConnectTimeout
=
connectTimeout
.
Value
;
var
watch
=
Stopwatch
.
StartNew
();
var
watch
=
Stopwatch
.
StartNew
();
var
task
=
ConnectionMultiplexer
.
ConnectAsync
(
config
,
log
??
Console
.
Out
);
var
task
=
ConnectionMultiplexer
.
ConnectAsync
(
config
,
log
??
Console
.
Out
);
if
(!
task
.
Wait
(
config
.
ConnectTimeout
*
2
))
if
(!
task
.
Wait
(
config
.
ConnectTimeout
>=
(
int
.
MaxValue
/
2
)
?
int
.
MaxValue
:
config
.
ConnectTimeout
*
2
))
{
{
task
.
ContinueWith
(
x
=>
task
.
ContinueWith
(
x
=>
{
{
...
...
StackExchange.Redis/StackExchange/Redis/DebuggingAids.cs
View file @
e931d6a7
...
@@ -6,6 +6,7 @@
...
@@ -6,6 +6,7 @@
using
System.Text.RegularExpressions
;
using
System.Text.RegularExpressions
;
using
System.Threading.Tasks
;
using
System.Threading.Tasks
;
using
System.Runtime.CompilerServices
;
using
System.Runtime.CompilerServices
;
using
System.Collections.Generic
;
namespace
StackExchange.Redis
namespace
StackExchange.Redis
{
{
...
...
StackExchange.Redis/StackExchange/Redis/IServer.cs
View file @
e931d6a7
...
@@ -253,6 +253,46 @@ public interface IServer : IRedis
...
@@ -253,6 +253,46 @@ public interface IServer : IRedis
/// <remarks>http://redis.io/topics/persistence</remarks>
/// <remarks>http://redis.io/topics/persistence</remarks>
Task
SaveAsync
(
SaveType
type
,
CommandFlags
flags
=
CommandFlags
.
None
);
Task
SaveAsync
(
SaveType
type
,
CommandFlags
flags
=
CommandFlags
.
None
);
/// <summary>
/// Inidicates whether the specified script is defined on the server
/// </summary>
bool
ScriptExists
(
string
script
,
CommandFlags
flags
=
CommandFlags
.
None
);
/// <summary>
/// Inidicates whether the specified script hash is defined on the server
/// </summary>
bool
ScriptExists
(
byte
[]
sha1
,
CommandFlags
flags
=
CommandFlags
.
None
);
/// <summary>
/// Inidicates whether the specified script is defined on the server
/// </summary>
Task
<
bool
>
ScriptExistsAsync
(
string
script
,
CommandFlags
flags
=
CommandFlags
.
None
);
/// <summary>
/// Inidicates whether the specified script hash is defined on the server
/// </summary>
Task
<
bool
>
ScriptExistsAsync
(
byte
[]
sha1
,
CommandFlags
flags
=
CommandFlags
.
None
);
/// <summary>
/// Removes all cached scripts on this server
/// </summary>
void
ScriptFlush
(
CommandFlags
flags
=
CommandFlags
.
None
);
/// <summary>
/// Removes all cached scripts on this server
/// </summary>
Task
ScriptFlushAsync
(
CommandFlags
flags
=
CommandFlags
.
None
);
/// <summary>
/// Explicitly defines a script on the server
/// </summary>
byte
[]
ScriptLoad
(
string
script
,
CommandFlags
flags
=
CommandFlags
.
None
);
/// <summary>
/// Explicitly defines a script on the server
/// </summary>
Task
<
byte
[
]>
ScriptLoadAsync
(
string
script
,
CommandFlags
flags
=
CommandFlags
.
None
);
/// <summary>Asks the redis server to shutdown, killing all connections. Please FULLY read the notes on the SHUTDOWN command.</summary>
/// <summary>Asks the redis server to shutdown, killing all connections. Please FULLY read the notes on the SHUTDOWN command.</summary>
/// <remarks>http://redis.io/commands/shutdown</remarks>
/// <remarks>http://redis.io/commands/shutdown</remarks>
void
Shutdown
(
ShutdownMode
shutdownMode
=
ShutdownMode
.
Default
,
CommandFlags
flags
=
CommandFlags
.
None
);
void
Shutdown
(
ShutdownMode
shutdownMode
=
ShutdownMode
.
Default
,
CommandFlags
flags
=
CommandFlags
.
None
);
...
...
StackExchange.Redis/StackExchange/Redis/Message.cs
View file @
e931d6a7
...
@@ -469,6 +469,7 @@ internal static bool RequiresDatabase(RedisCommand command)
...
@@ -469,6 +469,7 @@ internal static bool RequiresDatabase(RedisCommand command)
case
RedisCommand
.
READONLY
:
case
RedisCommand
.
READONLY
:
case
RedisCommand
.
READWRITE
:
case
RedisCommand
.
READWRITE
:
case
RedisCommand
.
SAVE
:
case
RedisCommand
.
SAVE
:
case
RedisCommand
.
SCRIPT
:
case
RedisCommand
.
SHUTDOWN
:
case
RedisCommand
.
SHUTDOWN
:
case
RedisCommand
.
SLAVEOF
:
case
RedisCommand
.
SLAVEOF
:
case
RedisCommand
.
SLOWLOG
:
case
RedisCommand
.
SLOWLOG
:
...
...
StackExchange.Redis/StackExchange/Redis/PhysicalBridge.cs
View file @
e931d6a7
...
@@ -280,7 +280,7 @@ internal void OnConnectionFailed(PhysicalConnection connection, ConnectionFailur
...
@@ -280,7 +280,7 @@ internal void OnConnectionFailed(PhysicalConnection connection, ConnectionFailur
}
}
}
}
internal
void
OnDisconnected
(
ConnectionFailureType
failureType
,
PhysicalConnection
connection
,
out
bool
isCurrent
)
internal
void
OnDisconnected
(
ConnectionFailureType
failureType
,
PhysicalConnection
connection
,
out
bool
isCurrent
,
out
State
oldState
)
{
{
Trace
(
"OnDisconnected"
);
Trace
(
"OnDisconnected"
);
...
@@ -294,11 +294,11 @@ internal void OnDisconnected(ConnectionFailureType failureType, PhysicalConnecti
...
@@ -294,11 +294,11 @@ internal void OnDisconnected(ConnectionFailureType failureType, PhysicalConnecti
ping
.
Fail
(
failureType
,
null
);
ping
.
Fail
(
failureType
,
null
);
CompleteSyncOrAsync
(
ping
);
CompleteSyncOrAsync
(
ping
);
}
}
oldState
=
default
(
State
);
// only defined when isCurrent = true
if
(
isCurrent
=
(
physical
==
connection
))
if
(
isCurrent
=
(
physical
==
connection
))
{
{
Trace
(
"Bridge noting disconnect from active connection"
+
(
isDisposed
?
" (disposed)"
:
""
));
Trace
(
"Bridge noting disconnect from active connection"
+
(
isDisposed
?
" (disposed)"
:
""
));
ChangeState
(
State
.
Disconnected
);
oldState
=
ChangeState
(
State
.
Disconnected
);
physical
=
null
;
physical
=
null
;
if
(!
isDisposed
&&
Interlocked
.
Increment
(
ref
failConnectCount
)
==
1
)
if
(!
isDisposed
&&
Interlocked
.
Increment
(
ref
failConnectCount
)
==
1
)
...
@@ -367,7 +367,8 @@ internal void OnHeartbeat()
...
@@ -367,7 +367,8 @@ internal void OnHeartbeat()
else
else
{
{
bool
ignore
;
bool
ignore
;
OnDisconnected
(
ConnectionFailureType
.
SocketFailure
,
tmp
,
out
ignore
);
State
oldState
;
OnDisconnected
(
ConnectionFailureType
.
SocketFailure
,
tmp
,
out
ignore
,
out
oldState
);
}
}
}
}
}
}
...
@@ -463,6 +464,7 @@ internal void WriteMessageDirect(PhysicalConnection tmp, Message next)
...
@@ -463,6 +464,7 @@ internal void WriteMessageDirect(PhysicalConnection tmp, Message next)
{
{
// we screwed up; abort; note that WriteMessageToServer already
// we screwed up; abort; note that WriteMessageToServer already
// killed the underlying connection
// killed the underlying connection
Trace
(
"Unable to write to server"
);
next
.
Fail
(
ConnectionFailureType
.
ProtocolFailure
,
null
);
next
.
Fail
(
ConnectionFailureType
.
ProtocolFailure
,
null
);
CompleteSyncOrAsync
(
next
);
CompleteSyncOrAsync
(
next
);
break
;
break
;
...
@@ -475,13 +477,14 @@ internal void WriteMessageDirect(PhysicalConnection tmp, Message next)
...
@@ -475,13 +477,14 @@ internal void WriteMessageDirect(PhysicalConnection tmp, Message next)
}
}
}
}
private
void
ChangeState
(
State
newState
)
private
State
ChangeState
(
State
newState
)
{
{
var
oldState
=
(
State
)
Interlocked
.
Exchange
(
ref
state
,
(
int
)
newState
);
var
oldState
=
(
State
)
Interlocked
.
Exchange
(
ref
state
,
(
int
)
newState
);
if
(
oldState
!=
newState
)
if
(
oldState
!=
newState
)
{
{
multiplexer
.
Trace
(
connectionType
+
" state changed from "
+
oldState
+
" to "
+
newState
);
multiplexer
.
Trace
(
connectionType
+
" state changed from "
+
oldState
+
" to "
+
newState
);
}
}
return
oldState
;
}
}
private
bool
ChangeState
(
State
oldState
,
State
newState
)
private
bool
ChangeState
(
State
oldState
,
State
newState
)
...
...
StackExchange.Redis/StackExchange/Redis/PhysicalConnection.cs
View file @
e931d6a7
...
@@ -71,7 +71,8 @@ private static readonly Message
...
@@ -71,7 +71,8 @@ private static readonly Message
public
PhysicalConnection
(
PhysicalBridge
bridge
)
public
PhysicalConnection
(
PhysicalBridge
bridge
)
{
{
lastWriteTickCount
=
lastReadTickCount
=
lastBeatTickCount
=
Environment
.
TickCount
;
lastWriteTickCount
=
lastReadTickCount
=
Environment
.
TickCount
;
lastBeatTickCount
=
0
;
this
.
connectionType
=
bridge
.
ConnectionType
;
this
.
connectionType
=
bridge
.
ConnectionType
;
this
.
multiplexer
=
bridge
.
Multiplexer
;
this
.
multiplexer
=
bridge
.
Multiplexer
;
this
.
ChannelPrefix
=
multiplexer
.
RawConfig
.
ChannelPrefix
;
this
.
ChannelPrefix
=
multiplexer
.
RawConfig
.
ChannelPrefix
;
...
@@ -151,7 +152,8 @@ public void RecordConnectionFailed(ConnectionFailureType failureType, Exception
...
@@ -151,7 +152,8 @@ public void RecordConnectionFailed(ConnectionFailureType failureType, Exception
// stop anything new coming in...
// stop anything new coming in...
bridge
.
Trace
(
"Failed: "
+
failureType
);
bridge
.
Trace
(
"Failed: "
+
failureType
);
bool
isCurrent
;
bool
isCurrent
;
bridge
.
OnDisconnected
(
failureType
,
this
,
out
isCurrent
);
PhysicalBridge
.
State
oldState
;
bridge
.
OnDisconnected
(
failureType
,
this
,
out
isCurrent
,
out
oldState
);
if
(
isCurrent
&&
Interlocked
.
CompareExchange
(
ref
failureReported
,
1
,
0
)
==
0
)
if
(
isCurrent
&&
Interlocked
.
CompareExchange
(
ref
failureReported
,
1
,
0
)
==
0
)
{
{
...
@@ -163,7 +165,7 @@ public void RecordConnectionFailed(ConnectionFailureType failureType, Exception
...
@@ -163,7 +165,7 @@ public void RecordConnectionFailed(ConnectionFailureType failureType, Exception
string
message
=
failureType
+
" on "
+
Format
.
ToString
(
bridge
.
ServerEndPoint
.
EndPoint
)
+
"/"
+
connectionType
string
message
=
failureType
+
" on "
+
Format
.
ToString
(
bridge
.
ServerEndPoint
.
EndPoint
)
+
"/"
+
connectionType
+
", input-buffer: "
+
ioBufferBytes
+
", outstanding: "
+
GetOutstandingCount
()
+
", input-buffer: "
+
ioBufferBytes
+
", outstanding: "
+
GetOutstandingCount
()
+
", last-read: "
+
unchecked
(
now
-
lastRead
)
/
1000
+
"s ago, last-write: "
+
unchecked
(
now
-
lastWrite
)
/
1000
+
"s ago, keep-alive: "
+
bridge
.
ServerEndPoint
.
WriteEverySeconds
+
"s, pending: "
+
", last-read: "
+
unchecked
(
now
-
lastRead
)
/
1000
+
"s ago, last-write: "
+
unchecked
(
now
-
lastWrite
)
/
1000
+
"s ago, keep-alive: "
+
bridge
.
ServerEndPoint
.
WriteEverySeconds
+
"s, pending: "
+
bridge
.
GetPendingCount
()
+
",
last-heartbeat: "
+
unchecked
(
now
-
lastBeat
)
/
1000
+
"s ago"
;
+
bridge
.
GetPendingCount
()
+
",
state: "
+
oldState
+
", last-heartbeat: "
+
(
lastBeat
==
0
?
"never"
:
(
unchecked
(
now
-
lastBeat
)
/
1000
+
"s ago"
))
;
var
ex
=
innerException
==
null
var
ex
=
innerException
==
null
?
new
RedisConnectionException
(
failureType
,
message
)
?
new
RedisConnectionException
(
failureType
,
message
)
...
...
StackExchange.Redis/StackExchange/Redis/RedisCommand.cs
View file @
e931d6a7
...
@@ -108,6 +108,7 @@ enum RedisCommand
...
@@ -108,6 +108,7 @@ enum RedisCommand
SAVE
,
SAVE
,
SCAN
,
SCAN
,
SCARD
,
SCARD
,
SCRIPT
,
SDIFF
,
SDIFF
,
SDIFFSTORE
,
SDIFFSTORE
,
SELECT
,
SELECT
,
...
...
StackExchange.Redis/StackExchange/Redis/RedisDatabase.cs
View file @
e931d6a7
...
@@ -676,13 +676,13 @@ public Task<RedisKey> RandomKeyAsync(CommandFlags flags = CommandFlags.None)
...
@@ -676,13 +676,13 @@ public Task<RedisKey> RandomKeyAsync(CommandFlags flags = CommandFlags.None)
public
RedisResult
ScriptEvaluate
(
string
script
,
RedisKey
[]
keys
=
null
,
RedisValue
[]
values
=
null
,
CommandFlags
flags
=
CommandFlags
.
None
)
public
RedisResult
ScriptEvaluate
(
string
script
,
RedisKey
[]
keys
=
null
,
RedisValue
[]
values
=
null
,
CommandFlags
flags
=
CommandFlags
.
None
)
{
{
var
msg
=
new
ScriptEvalMessage
(
Db
,
flags
,
RedisCommand
.
EVAL
,
script
,
keys
??
RedisKey
.
EmptyArray
,
values
??
RedisValue
.
EmptyArray
);
var
msg
=
new
ScriptEvalMessage
(
Db
,
flags
,
RedisCommand
.
EVAL
,
script
,
keys
??
RedisKey
.
EmptyArray
,
values
??
RedisValue
.
EmptyArray
);
return
ExecuteSync
(
msg
,
ResultProcessor
.
Redis
Result
);
return
ExecuteSync
(
msg
,
ResultProcessor
.
Script
Result
);
}
}
public
Task
<
RedisResult
>
ScriptEvaluateAsync
(
string
script
,
RedisKey
[]
keys
=
null
,
RedisValue
[]
values
=
null
,
CommandFlags
flags
=
CommandFlags
.
None
)
public
Task
<
RedisResult
>
ScriptEvaluateAsync
(
string
script
,
RedisKey
[]
keys
=
null
,
RedisValue
[]
values
=
null
,
CommandFlags
flags
=
CommandFlags
.
None
)
{
{
var
msg
=
new
ScriptEvalMessage
(
Db
,
flags
,
RedisCommand
.
EVAL
,
script
,
keys
??
RedisKey
.
EmptyArray
,
values
??
RedisValue
.
EmptyArray
);
var
msg
=
new
ScriptEvalMessage
(
Db
,
flags
,
RedisCommand
.
EVAL
,
script
,
keys
??
RedisKey
.
EmptyArray
,
values
??
RedisValue
.
EmptyArray
);
return
ExecuteAsync
(
msg
,
ResultProcessor
.
Redis
Result
);
return
ExecuteAsync
(
msg
,
ResultProcessor
.
Script
Result
);
}
}
public
bool
SetAdd
(
RedisKey
key
,
RedisValue
value
,
CommandFlags
flags
=
CommandFlags
.
None
)
public
bool
SetAdd
(
RedisKey
key
,
RedisValue
value
,
CommandFlags
flags
=
CommandFlags
.
None
)
...
@@ -1797,6 +1797,22 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
...
@@ -1797,6 +1797,22 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
}
}
}
}
internal
sealed
class
ScriptLoadMessage
:
Message
{
internal
readonly
string
Script
;
public
ScriptLoadMessage
(
CommandFlags
flags
,
string
script
)
:
base
(-
1
,
flags
,
RedisCommand
.
SCRIPT
)
{
if
(
script
==
null
)
throw
new
ArgumentNullException
(
"script"
);
this
.
Script
=
script
;
}
internal
override
void
WriteImpl
(
PhysicalConnection
physical
)
{
physical
.
WriteHeader
(
Command
,
2
);
physical
.
Write
(
RedisLiterals
.
LOAD
);
physical
.
Write
((
RedisValue
)
Script
);
}
}
internal
sealed
class
SetScanIterator
internal
sealed
class
SetScanIterator
{
{
internal
const
int
DefaultPageSize
=
10
;
internal
const
int
DefaultPageSize
=
10
;
...
@@ -1880,15 +1896,15 @@ Message CreateMessage(long cursor, bool running)
...
@@ -1880,15 +1896,15 @@ Message CreateMessage(long cursor, bool running)
}
}
}
}
}
}
private
sealed
class
ScriptEvalMessage
:
Message
,
IMultiMessage
private
sealed
class
ScriptEvalMessage
:
Message
{
{
private
readonly
RedisKey
[]
keys
;
private
readonly
RedisKey
[]
keys
;
private
readonly
RedisValue
script
;
private
readonly
string
script
;
private
readonly
RedisValue
[]
values
;
private
readonly
RedisValue
[]
values
;
private
RedisValue
hash
;
public
ScriptEvalMessage
(
int
db
,
CommandFlags
flags
,
RedisCommand
command
,
string
script
,
RedisKey
[]
keys
,
RedisValue
[]
values
)
:
base
(
db
,
flags
,
command
)
public
ScriptEvalMessage
(
int
db
,
CommandFlags
flags
,
RedisCommand
command
,
string
script
,
RedisKey
[]
keys
,
RedisValue
[]
values
)
:
base
(
db
,
flags
,
command
)
{
{
if
(
script
==
null
)
throw
new
ArgumentNullException
(
"script"
);
this
.
script
=
script
;
this
.
script
=
script
;
for
(
int
i
=
0
;
i
<
keys
.
Length
;
i
++)
for
(
int
i
=
0
;
i
<
keys
.
Length
;
i
++)
keys
[
i
].
Assert
();
keys
[
i
].
Assert
();
...
@@ -1905,10 +1921,31 @@ public override int GetHashSlot(ServerSelectionStrategy serverSelectionStrategy)
...
@@ -1905,10 +1921,31 @@ public override int GetHashSlot(ServerSelectionStrategy serverSelectionStrategy)
return
slot
;
return
slot
;
}
}
public
IEnumerable
<
Message
>
GetMessages
(
PhysicalConnection
connection
)
{
this
.
hash
=
connection
.
Bridge
.
ServerEndPoint
.
GetScriptHash
(
script
);
if
(
hash
.
IsNull
)
{
var
msg
=
new
ScriptLoadMessage
(
Flags
,
script
);
msg
.
SetInternalCall
();
msg
.
SetSource
(
ResultProcessor
.
ScriptLoad
,
null
);
yield
return
msg
;
}
yield
return
this
;
}
internal
override
void
WriteImpl
(
PhysicalConnection
physical
)
internal
override
void
WriteImpl
(
PhysicalConnection
physical
)
{
{
physical
.
WriteHeader
(
command
,
2
+
keys
.
Length
+
values
.
Length
);
if
(
hash
.
IsNull
)
physical
.
Write
(
script
);
{
physical
.
WriteHeader
(
RedisCommand
.
EVAL
,
2
+
keys
.
Length
+
values
.
Length
);
physical
.
Write
((
RedisValue
)
script
);
}
else
{
physical
.
WriteHeader
(
RedisCommand
.
EVALSHA
,
2
+
keys
.
Length
+
values
.
Length
);
physical
.
Write
(
hash
);
}
physical
.
Write
(
keys
.
Length
);
physical
.
Write
(
keys
.
Length
);
for
(
int
i
=
0
;
i
<
keys
.
Length
;
i
++)
for
(
int
i
=
0
;
i
<
keys
.
Length
;
i
++)
physical
.
Write
(
keys
[
i
]);
physical
.
Write
(
keys
[
i
]);
...
...
StackExchange.Redis/StackExchange/Redis/RedisLiterals.cs
View file @
e931d6a7
...
@@ -48,6 +48,9 @@ public static readonly RedisValue
...
@@ -48,6 +48,9 @@ public static readonly RedisValue
MIN
=
"MIN"
,
MIN
=
"MIN"
,
MAX
=
"MAX"
,
MAX
=
"MAX"
,
AGGREGATE
=
"AGGREGATE"
,
AGGREGATE
=
"AGGREGATE"
,
LOAD
=
"LOAD"
,
EXISTS
=
"EXISTS"
,
FLUSH
=
"FLUSH"
,
// DO NOT CHANGE CASE: these are configuration settings and MUST be as-is
// DO NOT CHANGE CASE: these are configuration settings and MUST be as-is
databases
=
"databases"
,
databases
=
"databases"
,
...
...
StackExchange.Redis/StackExchange/Redis/RedisServer.cs
View file @
e931d6a7
This diff is collapsed.
Click to expand it.
StackExchange.Redis/StackExchange/Redis/ResultProcessor.cs
View file @
e931d6a7
...
@@ -26,7 +26,8 @@ abstract class ResultProcessor
...
@@ -26,7 +26,8 @@ abstract class ResultProcessor
NullableDouble
=
new
NullableDoubleProcessor
();
NullableDouble
=
new
NullableDoubleProcessor
();
public
static
readonly
ResultProcessor
<
byte
[
]>
public
static
readonly
ResultProcessor
<
byte
[
]>
ByteArray
=
new
ByteArrayProcessor
();
ByteArray
=
new
ByteArrayProcessor
(),
ScriptLoad
=
new
ScriptLoadProcessor
();
public
static
readonly
ResultProcessor
<
ClusterConfiguration
>
public
static
readonly
ResultProcessor
<
ClusterConfiguration
>
ClusterNodes
=
new
ClusterNodesProcessor
();
ClusterNodes
=
new
ClusterNodesProcessor
();
...
@@ -81,7 +82,7 @@ public static readonly TimeSpanProcessor
...
@@ -81,7 +82,7 @@ public static readonly TimeSpanProcessor
SortedSetWithScores
=
new
SortedSetWithScoresProcessor
();
SortedSetWithScores
=
new
SortedSetWithScoresProcessor
();
public
static
readonly
ResultProcessor
<
RedisResult
>
public
static
readonly
ResultProcessor
<
RedisResult
>
RedisResult
=
new
Redis
ResultProcessor
();
ScriptResult
=
new
Script
ResultProcessor
();
static
readonly
byte
[]
MOVED
=
Encoding
.
UTF8
.
GetBytes
(
"MOVED "
),
ASK
=
Encoding
.
UTF8
.
GetBytes
(
"ASK "
);
static
readonly
byte
[]
MOVED
=
Encoding
.
UTF8
.
GetBytes
(
"MOVED "
),
ASK
=
Encoding
.
UTF8
.
GetBytes
(
"ASK "
);
...
@@ -505,6 +506,14 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
...
@@ -505,6 +506,14 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
case
ResultType
.
BulkString
:
case
ResultType
.
BulkString
:
SetResult
(
message
,
result
.
GetBoolean
());
SetResult
(
message
,
result
.
GetBoolean
());
return
true
;
return
true
;
case
ResultType
.
Array
:
var
items
=
result
.
GetItems
();
if
(
items
.
Length
==
1
)
{
// treat an array of 1 like a single reply (for example, SCRIPT EXISTS)
SetResult
(
message
,
items
[
0
].
GetBoolean
());
return
true
;
}
break
;
}
}
return
false
;
return
false
;
}
}
...
@@ -524,6 +533,28 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
...
@@ -524,6 +533,28 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
}
}
}
}
internal
sealed
class
ScriptLoadProcessor
:
ResultProcessor
<
byte
[
]>
{
// note that top-level error messages still get handled by SetResult, but nested errors
// (is that a thing?) will be wrapped in the RedisResult
protected
override
bool
SetResultCore
(
PhysicalConnection
connection
,
Message
message
,
RawResult
result
)
{
switch
(
result
.
Type
)
{
case
ResultType
.
BulkString
:
var
hash
=
result
.
GetBlob
();
var
sl
=
message
as
RedisDatabase
.
ScriptLoadMessage
;
if
(
sl
!=
null
)
{
connection
.
Bridge
.
ServerEndPoint
.
AddScript
(
sl
.
Script
,
hash
);
}
SetResult
(
message
,
hash
);
return
true
;
}
return
false
;
}
}
sealed
class
ClusterNodesProcessor
:
ResultProcessor
<
ClusterConfiguration
>
sealed
class
ClusterNodesProcessor
:
ResultProcessor
<
ClusterConfiguration
>
{
{
internal
static
ClusterConfiguration
Parse
(
PhysicalConnection
connection
,
string
nodes
)
internal
static
ClusterConfiguration
Parse
(
PhysicalConnection
connection
,
string
nodes
)
...
@@ -1012,8 +1043,19 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
...
@@ -1012,8 +1043,19 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
}
}
}
}
private
class
Redis
ResultProcessor
:
ResultProcessor
<
RedisResult
>
private
class
Script
ResultProcessor
:
ResultProcessor
<
RedisResult
>
{
{
static
readonly
byte
[]
NOSCRIPT
=
Encoding
.
UTF8
.
GetBytes
(
"NOSCRIPT "
);
public
override
bool
SetResult
(
PhysicalConnection
connection
,
Message
message
,
RawResult
result
)
{
if
(
result
.
Type
==
ResultType
.
Error
&&
result
.
AssertStarts
(
NOSCRIPT
))
{
// scripts are not flushed individually, so assume the entire script cache is toast ("SCRIPT FLUSH")
connection
.
Bridge
.
ServerEndPoint
.
FlushScripts
();
}
// and apply usual processing for the rest
return
base
.
SetResult
(
connection
,
message
,
result
);
}
// note that top-level error messages still get handled by SetResult, but nested errors
// note that top-level error messages still get handled by SetResult, but nested errors
// (is that a thing?) will be wrapped in the RedisResult
// (is that a thing?) will be wrapped in the RedisResult
protected
override
bool
SetResultCore
(
PhysicalConnection
connection
,
Message
message
,
RawResult
result
)
protected
override
bool
SetResultCore
(
PhysicalConnection
connection
,
Message
message
,
RawResult
result
)
...
...
StackExchange.Redis/StackExchange/Redis/ServerEndPoint.cs
View file @
e931d6a7
using
System
;
using
System
;
using
System.Collections
;
using
System.Collections.Generic
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Linq
;
using
System.Net
;
using
System.Net
;
...
@@ -525,5 +526,25 @@ internal string GetProfile()
...
@@ -525,5 +526,25 @@ internal string GetProfile()
if
(
tmp
!=
null
)
tmp
.
AppendProfile
(
sb
);
if
(
tmp
!=
null
)
tmp
.
AppendProfile
(
sb
);
return
sb
.
ToString
();
return
sb
.
ToString
();
}
}
private
readonly
Hashtable
knownScripts
=
new
Hashtable
(
StringComparer
.
Ordinal
);
internal
byte
[]
GetScriptHash
(
string
script
)
{
return
(
byte
[])
knownScripts
[
script
];
}
internal
void
AddScript
(
string
script
,
byte
[]
hash
)
{
lock
(
knownScripts
)
{
knownScripts
[
script
]
=
hash
;
}
}
internal
void
FlushScripts
()
{
lock
(
knownScripts
)
{
knownScripts
.
Clear
();
}
}
}
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment