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
3aebb0df
Commit
3aebb0df
authored
Feb 04, 2015
by
Marc Gravell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Whitespace reflow only
parent
94adafba
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
3367 additions
and
3367 deletions
+3367
-3367
PhysicalConnection.cs
...kExchange.Redis/StackExchange/Redis/PhysicalConnection.cs
+979
-979
RedisDatabase.cs
StackExchange.Redis/StackExchange/Redis/RedisDatabase.cs
+2388
-2388
No files found.
StackExchange.Redis/StackExchange/Redis/PhysicalConnection.cs
View file @
3aebb0df
using
System
;
using
System
;
using
System.Collections.Generic
;
using
System.Collections.Generic
;
using
System.IO
;
using
System.IO
;
using
System.Linq
;
using
System.Linq
;
using
System.Net
;
using
System.Net
;
using
System.Net.Security
;
using
System.Net.Security
;
using
System.Net.Sockets
;
using
System.Net.Sockets
;
using
System.Runtime.CompilerServices
;
using
System.Runtime.CompilerServices
;
using
System.Security.Authentication
;
using
System.Security.Authentication
;
using
System.Security.Cryptography.X509Certificates
;
using
System.Security.Cryptography.X509Certificates
;
using
System.Text
;
using
System.Text
;
using
System.Threading
;
using
System.Threading
;
namespace
StackExchange.Redis
namespace
StackExchange.Redis
{
{
internal
sealed
partial
class
PhysicalConnection
:
IDisposable
,
ISocketCallback
internal
sealed
partial
class
PhysicalConnection
:
IDisposable
,
ISocketCallback
{
{
internal
readonly
byte
[]
ChannelPrefix
;
internal
readonly
byte
[]
ChannelPrefix
;
private
const
int
DefaultRedisDatabaseCount
=
16
;
private
const
int
DefaultRedisDatabaseCount
=
16
;
private
static
readonly
byte
[]
Crlf
=
Encoding
.
ASCII
.
GetBytes
(
"\r\n"
);
private
static
readonly
byte
[]
Crlf
=
Encoding
.
ASCII
.
GetBytes
(
"\r\n"
);
static
readonly
AsyncCallback
endRead
=
result
=>
static
readonly
AsyncCallback
endRead
=
result
=>
{
{
PhysicalConnection
physical
;
PhysicalConnection
physical
;
if
(
result
.
CompletedSynchronously
||
(
physical
=
result
.
AsyncState
as
PhysicalConnection
)
==
null
)
return
;
if
(
result
.
CompletedSynchronously
||
(
physical
=
result
.
AsyncState
as
PhysicalConnection
)
==
null
)
return
;
try
try
{
{
physical
.
multiplexer
.
Trace
(
"Completed synchronously: processing in callback"
,
physical
.
physicalName
);
physical
.
multiplexer
.
Trace
(
"Completed synchronously: processing in callback"
,
physical
.
physicalName
);
if
(
physical
.
EndReading
(
result
))
physical
.
BeginReading
();
if
(
physical
.
EndReading
(
result
))
physical
.
BeginReading
();
}
}
catch
(
Exception
ex
)
catch
(
Exception
ex
)
{
{
physical
.
RecordConnectionFailed
(
ConnectionFailureType
.
InternalFailure
,
ex
);
physical
.
RecordConnectionFailed
(
ConnectionFailureType
.
InternalFailure
,
ex
);
}
}
};
};
private
static
readonly
byte
[]
message
=
Encoding
.
UTF8
.
GetBytes
(
"message"
),
pmessage
=
Encoding
.
UTF8
.
GetBytes
(
"pmessage"
);
private
static
readonly
byte
[]
message
=
Encoding
.
UTF8
.
GetBytes
(
"message"
),
pmessage
=
Encoding
.
UTF8
.
GetBytes
(
"pmessage"
);
static
readonly
Message
[]
ReusableChangeDatabaseCommands
=
Enumerable
.
Range
(
0
,
DefaultRedisDatabaseCount
).
Select
(
static
readonly
Message
[]
ReusableChangeDatabaseCommands
=
Enumerable
.
Range
(
0
,
DefaultRedisDatabaseCount
).
Select
(
i
=>
Message
.
Create
(
i
,
CommandFlags
.
FireAndForget
,
RedisCommand
.
SELECT
)).
ToArray
();
i
=>
Message
.
Create
(
i
,
CommandFlags
.
FireAndForget
,
RedisCommand
.
SELECT
)).
ToArray
();
private
static
readonly
Message
private
static
readonly
Message
ReusableReadOnlyCommand
=
Message
.
Create
(-
1
,
CommandFlags
.
FireAndForget
,
RedisCommand
.
READONLY
),
ReusableReadOnlyCommand
=
Message
.
Create
(-
1
,
CommandFlags
.
FireAndForget
,
RedisCommand
.
READONLY
),
ReusableReadWriteCommand
=
Message
.
Create
(-
1
,
CommandFlags
.
FireAndForget
,
RedisCommand
.
READWRITE
);
ReusableReadWriteCommand
=
Message
.
Create
(-
1
,
CommandFlags
.
FireAndForget
,
RedisCommand
.
READWRITE
);
private
static
int
totalCount
;
private
static
int
totalCount
;
private
readonly
PhysicalBridge
bridge
;
private
readonly
PhysicalBridge
bridge
;
private
readonly
ConnectionType
connectionType
;
private
readonly
ConnectionType
connectionType
;
private
readonly
ConnectionMultiplexer
multiplexer
;
private
readonly
ConnectionMultiplexer
multiplexer
;
// things sent to this physical, but not yet received
// things sent to this physical, but not yet received
private
readonly
Queue
<
Message
>
outstanding
=
new
Queue
<
Message
>();
private
readonly
Queue
<
Message
>
outstanding
=
new
Queue
<
Message
>();
readonly
string
physicalName
;
readonly
string
physicalName
;
volatile
int
currentDatabase
=
0
;
volatile
int
currentDatabase
=
0
;
ReadMode
currentReadMode
=
ReadMode
.
NotSpecified
;
ReadMode
currentReadMode
=
ReadMode
.
NotSpecified
;
int
failureReported
;
int
failureReported
;
byte
[]
ioBuffer
=
new
byte
[
512
];
byte
[]
ioBuffer
=
new
byte
[
512
];
int
ioBufferBytes
=
0
;
int
ioBufferBytes
=
0
;
int
lastWriteTickCount
,
lastReadTickCount
,
lastBeatTickCount
;
int
lastWriteTickCount
,
lastReadTickCount
,
lastBeatTickCount
;
int
firstUnansweredWriteTickCount
;
int
firstUnansweredWriteTickCount
;
private
Stream
netStream
,
outStream
;
private
Stream
netStream
,
outStream
;
private
SocketToken
socketToken
;
private
SocketToken
socketToken
;
public
PhysicalConnection
(
PhysicalBridge
bridge
)
public
PhysicalConnection
(
PhysicalBridge
bridge
)
{
{
lastWriteTickCount
=
lastReadTickCount
=
Environment
.
TickCount
;
lastWriteTickCount
=
lastReadTickCount
=
Environment
.
TickCount
;
lastBeatTickCount
=
0
;
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
;
if
(
this
.
ChannelPrefix
!=
null
&&
this
.
ChannelPrefix
.
Length
==
0
)
this
.
ChannelPrefix
=
null
;
// null tests are easier than null+empty
if
(
this
.
ChannelPrefix
!=
null
&&
this
.
ChannelPrefix
.
Length
==
0
)
this
.
ChannelPrefix
=
null
;
// null tests are easier than null+empty
var
endpoint
=
bridge
.
ServerEndPoint
.
EndPoint
;
var
endpoint
=
bridge
.
ServerEndPoint
.
EndPoint
;
physicalName
=
connectionType
+
"#"
+
Interlocked
.
Increment
(
ref
totalCount
)
+
"@"
+
Format
.
ToString
(
endpoint
);
physicalName
=
connectionType
+
"#"
+
Interlocked
.
Increment
(
ref
totalCount
)
+
"@"
+
Format
.
ToString
(
endpoint
);
this
.
bridge
=
bridge
;
this
.
bridge
=
bridge
;
OnCreateEcho
();
OnCreateEcho
();
}
}
public
void
BeginConnect
()
public
void
BeginConnect
()
{
{
Thread
.
VolatileWrite
(
ref
firstUnansweredWriteTickCount
,
0
);
Thread
.
VolatileWrite
(
ref
firstUnansweredWriteTickCount
,
0
);
var
endpoint
=
this
.
bridge
.
ServerEndPoint
.
EndPoint
;
var
endpoint
=
this
.
bridge
.
ServerEndPoint
.
EndPoint
;
multiplexer
.
Trace
(
"Connecting..."
,
physicalName
);
multiplexer
.
Trace
(
"Connecting..."
,
physicalName
);
this
.
socketToken
=
multiplexer
.
SocketManager
.
BeginConnect
(
endpoint
,
this
);
this
.
socketToken
=
multiplexer
.
SocketManager
.
BeginConnect
(
endpoint
,
this
);
}
}
private
enum
ReadMode
:
byte
private
enum
ReadMode
:
byte
{
{
NotSpecified
,
NotSpecified
,
ReadOnly
,
ReadOnly
,
ReadWrite
ReadWrite
}
}
public
PhysicalBridge
Bridge
{
get
{
return
bridge
;
}
}
public
PhysicalBridge
Bridge
{
get
{
return
bridge
;
}
}
public
long
LastWriteSecondsAgo
public
long
LastWriteSecondsAgo
{
{
get
get
{
{
return
unchecked
(
Environment
.
TickCount
-
Thread
.
VolatileRead
(
ref
lastWriteTickCount
))
/
1000
;
return
unchecked
(
Environment
.
TickCount
-
Thread
.
VolatileRead
(
ref
lastWriteTickCount
))
/
1000
;
}
}
}
}
public
ConnectionMultiplexer
Multiplexer
{
get
{
return
multiplexer
;
}
}
public
ConnectionMultiplexer
Multiplexer
{
get
{
return
multiplexer
;
}
}
public
long
SubscriptionCount
{
get
;
set
;
}
public
long
SubscriptionCount
{
get
;
set
;
}
public
bool
TransactionActive
{
get
;
internal
set
;
}
public
bool
TransactionActive
{
get
;
internal
set
;
}
public
void
Dispose
()
public
void
Dispose
()
{
{
if
(
outStream
!=
null
)
if
(
outStream
!=
null
)
{
{
multiplexer
.
Trace
(
"Disconnecting..."
,
physicalName
);
multiplexer
.
Trace
(
"Disconnecting..."
,
physicalName
);
try
{
outStream
.
Close
();
}
catch
{
}
try
{
outStream
.
Close
();
}
catch
{
}
try
{
outStream
.
Dispose
();
}
catch
{
}
try
{
outStream
.
Dispose
();
}
catch
{
}
outStream
=
null
;
outStream
=
null
;
}
}
if
(
netStream
!=
null
)
if
(
netStream
!=
null
)
{
{
try
{
netStream
.
Close
();
}
catch
{
}
try
{
netStream
.
Close
();
}
catch
{
}
try
{
netStream
.
Dispose
();
}
catch
{
}
try
{
netStream
.
Dispose
();
}
catch
{
}
netStream
=
null
;
netStream
=
null
;
}
}
if
(
socketToken
.
HasValue
)
if
(
socketToken
.
HasValue
)
{
{
var
socketManager
=
multiplexer
.
SocketManager
;
var
socketManager
=
multiplexer
.
SocketManager
;
if
(
socketManager
!=
null
)
socketManager
.
Shutdown
(
socketToken
);
if
(
socketManager
!=
null
)
socketManager
.
Shutdown
(
socketToken
);
socketToken
=
default
(
SocketToken
);
socketToken
=
default
(
SocketToken
);
multiplexer
.
Trace
(
"Disconnected"
,
physicalName
);
multiplexer
.
Trace
(
"Disconnected"
,
physicalName
);
RecordConnectionFailed
(
ConnectionFailureType
.
ConnectionDisposed
);
RecordConnectionFailed
(
ConnectionFailureType
.
ConnectionDisposed
);
}
}
OnCloseEcho
();
OnCloseEcho
();
}
}
public
void
Flush
()
public
void
Flush
()
{
{
var
tmp
=
outStream
;
var
tmp
=
outStream
;
if
(
tmp
!=
null
)
if
(
tmp
!=
null
)
{
{
tmp
.
Flush
();
tmp
.
Flush
();
Interlocked
.
Exchange
(
ref
lastWriteTickCount
,
Environment
.
TickCount
);
Interlocked
.
Exchange
(
ref
lastWriteTickCount
,
Environment
.
TickCount
);
}
}
}
}
public
void
RecordConnectionFailed
(
ConnectionFailureType
failureType
,
Exception
innerException
=
null
,
[
CallerMemberName
]
string
origin
=
null
)
public
void
RecordConnectionFailed
(
ConnectionFailureType
failureType
,
Exception
innerException
=
null
,
[
CallerMemberName
]
string
origin
=
null
)
{
{
IdentifyFailureType
(
innerException
,
ref
failureType
);
IdentifyFailureType
(
innerException
,
ref
failureType
);
if
(
failureType
==
ConnectionFailureType
.
InternalFailure
)
OnInternalError
(
innerException
,
origin
);
if
(
failureType
==
ConnectionFailureType
.
InternalFailure
)
OnInternalError
(
innerException
,
origin
);
// stop anything new coming in...
// stop anything new coming in...
bridge
.
Trace
(
"Failed: "
+
failureType
);
bridge
.
Trace
(
"Failed: "
+
failureType
);
bool
isCurrent
;
bool
isCurrent
;
PhysicalBridge
.
State
oldState
;
PhysicalBridge
.
State
oldState
;
bridge
.
OnDisconnected
(
failureType
,
this
,
out
isCurrent
,
out
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
)
{
{
int
now
=
Environment
.
TickCount
,
lastRead
=
Thread
.
VolatileRead
(
ref
lastReadTickCount
),
lastWrite
=
Thread
.
VolatileRead
(
ref
lastWriteTickCount
),
int
now
=
Environment
.
TickCount
,
lastRead
=
Thread
.
VolatileRead
(
ref
lastReadTickCount
),
lastWrite
=
Thread
.
VolatileRead
(
ref
lastWriteTickCount
),
lastBeat
=
Thread
.
VolatileRead
(
ref
lastBeatTickCount
);
lastBeat
=
Thread
.
VolatileRead
(
ref
lastBeatTickCount
);
int
unansweredRead
=
Thread
.
VolatileRead
(
ref
firstUnansweredWriteTickCount
);
int
unansweredRead
=
Thread
.
VolatileRead
(
ref
firstUnansweredWriteTickCount
);
string
message
=
failureType
+
" on "
+
Format
.
ToString
(
bridge
.
ServerEndPoint
.
EndPoint
)
+
"/"
+
connectionType
string
message
=
failureType
+
" on "
+
Format
.
ToString
(
bridge
.
ServerEndPoint
.
EndPoint
)
+
"/"
+
connectionType
+
", input-buffer: "
+
ioBufferBytes
+
", outstanding: "
+
GetSentAwaitingResponseCount
()
+
", input-buffer: "
+
ioBufferBytes
+
", outstanding: "
+
GetSentAwaitingResponseCount
()
+
", last-read: "
+
unchecked
(
now
-
lastRead
)
/
1000
+
"s ago, last-write: "
+
unchecked
(
now
-
lastWrite
)
/
1000
+
"s ago"
+
", last-read: "
+
unchecked
(
now
-
lastRead
)
/
1000
+
"s ago, last-write: "
+
unchecked
(
now
-
lastWrite
)
/
1000
+
"s ago"
+
", unanswered-write: "
+
unchecked
(
now
-
unansweredRead
)
/
1000
+
"s ago"
+
", unanswered-write: "
+
unchecked
(
now
-
unansweredRead
)
/
1000
+
"s ago"
+
", keep-alive: "
+
bridge
.
ServerEndPoint
.
WriteEverySeconds
+
"s, pending: "
+
", keep-alive: "
+
bridge
.
ServerEndPoint
.
WriteEverySeconds
+
"s, pending: "
+
bridge
.
GetPendingCount
()
+
", state: "
+
oldState
+
", last-heartbeat: "
+
(
lastBeat
==
0
?
"never"
:
(
unchecked
(
now
-
lastBeat
)
/
1000
+
"s ago"
))
+
bridge
.
GetPendingCount
()
+
", state: "
+
oldState
+
", last-heartbeat: "
+
(
lastBeat
==
0
?
"never"
:
(
unchecked
(
now
-
lastBeat
)
/
1000
+
"s ago"
))
+
(
bridge
.
IsBeating
?
" (mid-beat)"
:
""
)
+
", last-mbeat: "
+
multiplexer
.
LastHeartbeatSecondsAgo
+
"s ago, global: "
+
(
bridge
.
IsBeating
?
" (mid-beat)"
:
""
)
+
", last-mbeat: "
+
multiplexer
.
LastHeartbeatSecondsAgo
+
"s ago, global: "
+
ConnectionMultiplexer
.
LastGlobalHeartbeatSecondsAgo
+
"s ago"
;
+
ConnectionMultiplexer
.
LastGlobalHeartbeatSecondsAgo
+
"s ago"
;
var
ex
=
innerException
==
null
var
ex
=
innerException
==
null
?
new
RedisConnectionException
(
failureType
,
message
)
?
new
RedisConnectionException
(
failureType
,
message
)
:
new
RedisConnectionException
(
failureType
,
message
,
innerException
);
:
new
RedisConnectionException
(
failureType
,
message
,
innerException
);
bridge
.
OnConnectionFailed
(
this
,
failureType
,
ex
);
bridge
.
OnConnectionFailed
(
this
,
failureType
,
ex
);
}
}
// cleanup
// cleanup
lock
(
outstanding
)
lock
(
outstanding
)
{
{
bridge
.
Trace
(
outstanding
.
Count
!=
0
,
"Failing outstanding messages: "
+
outstanding
.
Count
);
bridge
.
Trace
(
outstanding
.
Count
!=
0
,
"Failing outstanding messages: "
+
outstanding
.
Count
);
while
(
outstanding
.
Count
!=
0
)
while
(
outstanding
.
Count
!=
0
)
{
{
var
next
=
outstanding
.
Dequeue
();
var
next
=
outstanding
.
Dequeue
();
bridge
.
Trace
(
"Failing: "
+
next
);
bridge
.
Trace
(
"Failing: "
+
next
);
next
.
Fail
(
failureType
,
innerException
);
next
.
Fail
(
failureType
,
innerException
);
bridge
.
CompleteSyncOrAsync
(
next
);
bridge
.
CompleteSyncOrAsync
(
next
);
}
}
}
}
// burn the socket
// burn the socket
var
socketManager
=
multiplexer
.
SocketManager
;
var
socketManager
=
multiplexer
.
SocketManager
;
if
(
socketManager
!=
null
)
socketManager
.
Shutdown
(
socketToken
);
if
(
socketManager
!=
null
)
socketManager
.
Shutdown
(
socketToken
);
}
}
public
override
string
ToString
()
public
override
string
ToString
()
{
{
return
physicalName
;
return
physicalName
;
}
}
internal
static
void
IdentifyFailureType
(
Exception
exception
,
ref
ConnectionFailureType
failureType
)
internal
static
void
IdentifyFailureType
(
Exception
exception
,
ref
ConnectionFailureType
failureType
)
{
{
if
(
exception
!=
null
&&
failureType
==
ConnectionFailureType
.
InternalFailure
)
if
(
exception
!=
null
&&
failureType
==
ConnectionFailureType
.
InternalFailure
)
{
{
if
(
exception
is
AuthenticationException
)
failureType
=
ConnectionFailureType
.
AuthenticationFailure
;
if
(
exception
is
AuthenticationException
)
failureType
=
ConnectionFailureType
.
AuthenticationFailure
;
else
if
(
exception
is
SocketException
||
exception
is
IOException
)
failureType
=
ConnectionFailureType
.
SocketFailure
;
else
if
(
exception
is
SocketException
||
exception
is
IOException
)
failureType
=
ConnectionFailureType
.
SocketFailure
;
else
if
(
exception
is
EndOfStreamException
)
failureType
=
ConnectionFailureType
.
SocketClosed
;
else
if
(
exception
is
EndOfStreamException
)
failureType
=
ConnectionFailureType
.
SocketClosed
;
else
if
(
exception
is
ObjectDisposedException
)
failureType
=
ConnectionFailureType
.
SocketClosed
;
else
if
(
exception
is
ObjectDisposedException
)
failureType
=
ConnectionFailureType
.
SocketClosed
;
}
}
}
}
internal
void
Enqueue
(
Message
next
)
internal
void
Enqueue
(
Message
next
)
{
{
lock
(
outstanding
)
lock
(
outstanding
)
{
{
outstanding
.
Enqueue
(
next
);
outstanding
.
Enqueue
(
next
);
}
}
}
}
internal
void
GetCounters
(
ConnectionCounters
counters
)
internal
void
GetCounters
(
ConnectionCounters
counters
)
{
{
lock
(
outstanding
)
lock
(
outstanding
)
{
{
counters
.
SentItemsAwaitingResponse
=
outstanding
.
Count
;
counters
.
SentItemsAwaitingResponse
=
outstanding
.
Count
;
}
}
counters
.
Subscriptions
=
SubscriptionCount
;
counters
.
Subscriptions
=
SubscriptionCount
;
}
}
internal
Message
GetReadModeCommand
(
bool
isMasterOnly
)
internal
Message
GetReadModeCommand
(
bool
isMasterOnly
)
{
{
var
serverEndpoint
=
bridge
.
ServerEndPoint
;
var
serverEndpoint
=
bridge
.
ServerEndPoint
;
if
(
serverEndpoint
.
RequiresReadMode
)
if
(
serverEndpoint
.
RequiresReadMode
)
{
{
ReadMode
requiredReadMode
=
isMasterOnly
?
ReadMode
.
ReadWrite
:
ReadMode
.
ReadOnly
;
ReadMode
requiredReadMode
=
isMasterOnly
?
ReadMode
.
ReadWrite
:
ReadMode
.
ReadOnly
;
if
(
requiredReadMode
!=
currentReadMode
)
if
(
requiredReadMode
!=
currentReadMode
)
{
{
currentReadMode
=
requiredReadMode
;
currentReadMode
=
requiredReadMode
;
switch
(
requiredReadMode
)
switch
(
requiredReadMode
)
{
{
case
ReadMode
.
ReadOnly
:
return
ReusableReadOnlyCommand
;
case
ReadMode
.
ReadOnly
:
return
ReusableReadOnlyCommand
;
case
ReadMode
.
ReadWrite
:
return
ReusableReadWriteCommand
;
case
ReadMode
.
ReadWrite
:
return
ReusableReadWriteCommand
;
}
}
}
}
}
}
else
if
(
currentReadMode
==
ReadMode
.
ReadOnly
)
else
if
(
currentReadMode
==
ReadMode
.
ReadOnly
)
{
// we don't need it (because we're not a cluster, or not a slave),
{
// we don't need it (because we're not a cluster, or not a slave),
// but we are in read-only mode; switch to read-write
// but we are in read-only mode; switch to read-write
currentReadMode
=
ReadMode
.
ReadWrite
;
currentReadMode
=
ReadMode
.
ReadWrite
;
return
ReusableReadWriteCommand
;
return
ReusableReadWriteCommand
;
}
}
return
null
;
return
null
;
}
}
internal
Message
GetSelectDatabaseCommand
(
int
targetDatabase
,
Message
message
)
internal
Message
GetSelectDatabaseCommand
(
int
targetDatabase
,
Message
message
)
{
{
if
(
targetDatabase
<
0
)
return
null
;
if
(
targetDatabase
<
0
)
return
null
;
if
(
targetDatabase
!=
currentDatabase
)
if
(
targetDatabase
!=
currentDatabase
)
{
{
var
serverEndpoint
=
bridge
.
ServerEndPoint
;
var
serverEndpoint
=
bridge
.
ServerEndPoint
;
int
available
=
serverEndpoint
.
Databases
;
int
available
=
serverEndpoint
.
Databases
;
if
(!
serverEndpoint
.
HasDatabases
)
// only db0 is available on cluster/twemproxy
if
(!
serverEndpoint
.
HasDatabases
)
// only db0 is available on cluster/twemproxy
{
{
if
(
targetDatabase
!=
0
)
if
(
targetDatabase
!=
0
)
{
// should never see this, since the API doesn't allow it; thus not too worried about ExceptionFactory
{
// should never see this, since the API doesn't allow it; thus not too worried about ExceptionFactory
throw
new
RedisCommandException
(
"Multiple databases are not supported on this server; cannot switch to database: "
+
targetDatabase
);
throw
new
RedisCommandException
(
"Multiple databases are not supported on this server; cannot switch to database: "
+
targetDatabase
);
}
}
return
null
;
return
null
;
}
}
if
(
message
.
Command
==
RedisCommand
.
SELECT
)
if
(
message
.
Command
==
RedisCommand
.
SELECT
)
{
{
// this could come from an EVAL/EVALSHA inside a transaction, for example; we'll accept it
// this could come from an EVAL/EVALSHA inside a transaction, for example; we'll accept it
bridge
.
Trace
(
"Switching database: "
+
targetDatabase
);
bridge
.
Trace
(
"Switching database: "
+
targetDatabase
);
currentDatabase
=
targetDatabase
;
currentDatabase
=
targetDatabase
;
return
null
;
return
null
;
}
}
if
(
TransactionActive
)
if
(
TransactionActive
)
{
// should never see this, since the API doesn't allow it; thus not too worried about ExceptionFactory
{
// should never see this, since the API doesn't allow it; thus not too worried about ExceptionFactory
throw
new
RedisCommandException
(
"Multiple databases inside a transaction are not currently supported: "
+
targetDatabase
);
throw
new
RedisCommandException
(
"Multiple databases inside a transaction are not currently supported: "
+
targetDatabase
);
}
}
if
(
available
!=
0
&&
targetDatabase
>=
available
)
// we positively know it is out of range
if
(
available
!=
0
&&
targetDatabase
>=
available
)
// we positively know it is out of range
{
{
throw
ExceptionFactory
.
DatabaseOutfRange
(
multiplexer
.
IncludeDetailInExceptions
,
targetDatabase
,
message
,
serverEndpoint
);
throw
ExceptionFactory
.
DatabaseOutfRange
(
multiplexer
.
IncludeDetailInExceptions
,
targetDatabase
,
message
,
serverEndpoint
);
}
}
bridge
.
Trace
(
"Switching database: "
+
targetDatabase
);
bridge
.
Trace
(
"Switching database: "
+
targetDatabase
);
currentDatabase
=
targetDatabase
;
currentDatabase
=
targetDatabase
;
return
GetSelectDatabaseCommand
(
targetDatabase
);
return
GetSelectDatabaseCommand
(
targetDatabase
);
}
}
return
null
;
return
null
;
}
}
internal
static
Message
GetSelectDatabaseCommand
(
int
targetDatabase
)
internal
static
Message
GetSelectDatabaseCommand
(
int
targetDatabase
)
{
{
return
targetDatabase
<
DefaultRedisDatabaseCount
return
targetDatabase
<
DefaultRedisDatabaseCount
?
ReusableChangeDatabaseCommands
[
targetDatabase
]
// 0-15 by default
?
ReusableChangeDatabaseCommands
[
targetDatabase
]
// 0-15 by default
:
Message
.
Create
(
targetDatabase
,
CommandFlags
.
FireAndForget
,
RedisCommand
.
SELECT
);
:
Message
.
Create
(
targetDatabase
,
CommandFlags
.
FireAndForget
,
RedisCommand
.
SELECT
);
}
}
internal
int
GetSentAwaitingResponseCount
()
internal
int
GetSentAwaitingResponseCount
()
{
{
lock
(
outstanding
)
lock
(
outstanding
)
{
{
return
outstanding
.
Count
;
return
outstanding
.
Count
;
}
}
}
}
internal
void
GetStormLog
(
StringBuilder
sb
)
internal
void
GetStormLog
(
StringBuilder
sb
)
{
{
lock
(
outstanding
)
lock
(
outstanding
)
{
{
if
(
outstanding
.
Count
==
0
)
return
;
if
(
outstanding
.
Count
==
0
)
return
;
sb
.
Append
(
"Sent, awaiting response from server: "
).
Append
(
outstanding
.
Count
).
AppendLine
();
sb
.
Append
(
"Sent, awaiting response from server: "
).
Append
(
outstanding
.
Count
).
AppendLine
();
int
total
=
0
;
int
total
=
0
;
foreach
(
var
item
in
outstanding
)
foreach
(
var
item
in
outstanding
)
{
{
if
(++
total
>=
500
)
break
;
if
(++
total
>=
500
)
break
;
item
.
AppendStormLog
(
sb
);
item
.
AppendStormLog
(
sb
);
sb
.
AppendLine
();
sb
.
AppendLine
();
}
}
}
}
}
}
internal
void
OnHeartbeat
()
internal
void
OnHeartbeat
()
{
{
Interlocked
.
Exchange
(
ref
lastBeatTickCount
,
Environment
.
TickCount
);
Interlocked
.
Exchange
(
ref
lastBeatTickCount
,
Environment
.
TickCount
);
}
}
internal
void
OnInternalError
(
Exception
exception
,
[
CallerMemberName
]
string
origin
=
null
)
internal
void
OnInternalError
(
Exception
exception
,
[
CallerMemberName
]
string
origin
=
null
)
{
{
multiplexer
.
OnInternalError
(
exception
,
bridge
.
ServerEndPoint
.
EndPoint
,
connectionType
,
origin
);
multiplexer
.
OnInternalError
(
exception
,
bridge
.
ServerEndPoint
.
EndPoint
,
connectionType
,
origin
);
}
}
internal
void
SetUnknownDatabase
()
internal
void
SetUnknownDatabase
()
{
// forces next db-specific command to issue a select
{
// forces next db-specific command to issue a select
currentDatabase
=
-
1
;
currentDatabase
=
-
1
;
}
}
internal
void
Write
(
RedisKey
key
)
internal
void
Write
(
RedisKey
key
)
{
{
var
val
=
key
.
KeyValue
;
var
val
=
key
.
KeyValue
;
if
(
val
is
string
)
if
(
val
is
string
)
{
{
WriteUnified
(
outStream
,
key
.
KeyPrefix
,
(
string
)
val
);
WriteUnified
(
outStream
,
key
.
KeyPrefix
,
(
string
)
val
);
}
}
else
else
{
{
WriteUnified
(
outStream
,
key
.
KeyPrefix
,
(
byte
[])
val
);
WriteUnified
(
outStream
,
key
.
KeyPrefix
,
(
byte
[])
val
);
}
}
}
}
internal
void
Write
(
RedisChannel
channel
)
internal
void
Write
(
RedisChannel
channel
)
{
{
WriteUnified
(
outStream
,
ChannelPrefix
,
channel
.
Value
);
WriteUnified
(
outStream
,
ChannelPrefix
,
channel
.
Value
);
}
}
internal
void
Write
(
RedisValue
value
)
internal
void
Write
(
RedisValue
value
)
{
{
if
(
value
.
IsInteger
)
if
(
value
.
IsInteger
)
{
{
WriteUnified
(
outStream
,
(
long
)
value
);
WriteUnified
(
outStream
,
(
long
)
value
);
}
}
else
else
{
{
WriteUnified
(
outStream
,
(
byte
[])
value
);
WriteUnified
(
outStream
,
(
byte
[])
value
);
}
}
}
}
internal
void
WriteHeader
(
RedisCommand
command
,
int
arguments
)
internal
void
WriteHeader
(
RedisCommand
command
,
int
arguments
)
{
{
var
commandBytes
=
multiplexer
.
CommandMap
.
GetBytes
(
command
);
var
commandBytes
=
multiplexer
.
CommandMap
.
GetBytes
(
command
);
if
(
commandBytes
==
null
)
if
(
commandBytes
==
null
)
{
{
throw
ExceptionFactory
.
CommandDisabled
(
multiplexer
.
IncludeDetailInExceptions
,
command
,
null
,
bridge
.
ServerEndPoint
);
throw
ExceptionFactory
.
CommandDisabled
(
multiplexer
.
IncludeDetailInExceptions
,
command
,
null
,
bridge
.
ServerEndPoint
);
}
}
outStream
.
WriteByte
((
byte
)
'*'
);
outStream
.
WriteByte
((
byte
)
'*'
);
// remember the time of the first write that still not followed by read
// remember the time of the first write that still not followed by read
Interlocked
.
CompareExchange
(
ref
firstUnansweredWriteTickCount
,
Environment
.
TickCount
,
0
);
Interlocked
.
CompareExchange
(
ref
firstUnansweredWriteTickCount
,
Environment
.
TickCount
,
0
);
WriteRaw
(
outStream
,
arguments
+
1
);
WriteRaw
(
outStream
,
arguments
+
1
);
WriteUnified
(
outStream
,
commandBytes
);
WriteUnified
(
outStream
,
commandBytes
);
}
}
static
void
WriteRaw
(
Stream
stream
,
long
value
,
bool
withLengthPrefix
=
false
)
static
void
WriteRaw
(
Stream
stream
,
long
value
,
bool
withLengthPrefix
=
false
)
{
{
if
(
value
>=
0
&&
value
<=
9
)
if
(
value
>=
0
&&
value
<=
9
)
{
{
if
(
withLengthPrefix
)
if
(
withLengthPrefix
)
{
{
stream
.
WriteByte
((
byte
)
'1'
);
stream
.
WriteByte
((
byte
)
'1'
);
stream
.
Write
(
Crlf
,
0
,
2
);
stream
.
Write
(
Crlf
,
0
,
2
);
}
}
stream
.
WriteByte
((
byte
)((
int
)
'0'
+
(
int
)
value
));
stream
.
WriteByte
((
byte
)((
int
)
'0'
+
(
int
)
value
));
}
}
else
if
(
value
>=
10
&&
value
<
100
)
else
if
(
value
>=
10
&&
value
<
100
)
{
{
if
(
withLengthPrefix
)
if
(
withLengthPrefix
)
{
{
stream
.
WriteByte
((
byte
)
'2'
);
stream
.
WriteByte
((
byte
)
'2'
);
stream
.
Write
(
Crlf
,
0
,
2
);
stream
.
Write
(
Crlf
,
0
,
2
);
}
}
stream
.
WriteByte
((
byte
)((
int
)
'0'
+
(
int
)
value
/
10
));
stream
.
WriteByte
((
byte
)((
int
)
'0'
+
(
int
)
value
/
10
));
stream
.
WriteByte
((
byte
)((
int
)
'0'
+
(
int
)
value
%
10
));
stream
.
WriteByte
((
byte
)((
int
)
'0'
+
(
int
)
value
%
10
));
}
}
else
if
(
value
>=
100
&&
value
<
1000
)
else
if
(
value
>=
100
&&
value
<
1000
)
{
{
int
v
=
(
int
)
value
;
int
v
=
(
int
)
value
;
int
units
=
v
%
10
;
int
units
=
v
%
10
;
v
/=
10
;
v
/=
10
;
int
tens
=
v
%
10
,
hundreds
=
v
/
10
;
int
tens
=
v
%
10
,
hundreds
=
v
/
10
;
if
(
withLengthPrefix
)
if
(
withLengthPrefix
)
{
{
stream
.
WriteByte
((
byte
)
'3'
);
stream
.
WriteByte
((
byte
)
'3'
);
stream
.
Write
(
Crlf
,
0
,
2
);
stream
.
Write
(
Crlf
,
0
,
2
);
}
}
stream
.
WriteByte
((
byte
)((
int
)
'0'
+
hundreds
));
stream
.
WriteByte
((
byte
)((
int
)
'0'
+
hundreds
));
stream
.
WriteByte
((
byte
)((
int
)
'0'
+
tens
));
stream
.
WriteByte
((
byte
)((
int
)
'0'
+
tens
));
stream
.
WriteByte
((
byte
)((
int
)
'0'
+
units
));
stream
.
WriteByte
((
byte
)((
int
)
'0'
+
units
));
}
}
else
if
(
value
<
0
&&
value
>=
-
9
)
else
if
(
value
<
0
&&
value
>=
-
9
)
{
{
if
(
withLengthPrefix
)
if
(
withLengthPrefix
)
{
{
stream
.
WriteByte
((
byte
)
'2'
);
stream
.
WriteByte
((
byte
)
'2'
);
stream
.
Write
(
Crlf
,
0
,
2
);
stream
.
Write
(
Crlf
,
0
,
2
);
}
}
stream
.
WriteByte
((
byte
)
'-'
);
stream
.
WriteByte
((
byte
)
'-'
);
stream
.
WriteByte
((
byte
)((
int
)
'0'
-
(
int
)
value
));
stream
.
WriteByte
((
byte
)((
int
)
'0'
-
(
int
)
value
));
}
}
else
if
(
value
<=
-
10
&&
value
>
-
100
)
else
if
(
value
<=
-
10
&&
value
>
-
100
)
{
{
if
(
withLengthPrefix
)
if
(
withLengthPrefix
)
{
{
stream
.
WriteByte
((
byte
)
'3'
);
stream
.
WriteByte
((
byte
)
'3'
);
stream
.
Write
(
Crlf
,
0
,
2
);
stream
.
Write
(
Crlf
,
0
,
2
);
}
}
value
=
-
value
;
value
=
-
value
;
stream
.
WriteByte
((
byte
)
'-'
);
stream
.
WriteByte
((
byte
)
'-'
);
stream
.
WriteByte
((
byte
)((
int
)
'0'
+
(
int
)
value
/
10
));
stream
.
WriteByte
((
byte
)((
int
)
'0'
+
(
int
)
value
/
10
));
stream
.
WriteByte
((
byte
)((
int
)
'0'
+
(
int
)
value
%
10
));
stream
.
WriteByte
((
byte
)((
int
)
'0'
+
(
int
)
value
%
10
));
}
}
else
else
{
{
var
bytes
=
Encoding
.
ASCII
.
GetBytes
(
Format
.
ToString
(
value
));
var
bytes
=
Encoding
.
ASCII
.
GetBytes
(
Format
.
ToString
(
value
));
if
(
withLengthPrefix
)
if
(
withLengthPrefix
)
{
{
WriteRaw
(
stream
,
bytes
.
Length
,
false
);
WriteRaw
(
stream
,
bytes
.
Length
,
false
);
}
}
stream
.
Write
(
bytes
,
0
,
bytes
.
Length
);
stream
.
Write
(
bytes
,
0
,
bytes
.
Length
);
}
}
stream
.
Write
(
Crlf
,
0
,
2
);
stream
.
Write
(
Crlf
,
0
,
2
);
}
}
static
void
WriteUnified
(
Stream
stream
,
byte
[]
value
)
static
void
WriteUnified
(
Stream
stream
,
byte
[]
value
)
{
{
stream
.
WriteByte
((
byte
)
'$'
);
stream
.
WriteByte
((
byte
)
'$'
);
if
(
value
==
null
)
if
(
value
==
null
)
{
{
WriteRaw
(
stream
,
-
1
);
// note that not many things like this...
WriteRaw
(
stream
,
-
1
);
// note that not many things like this...
}
}
else
else
{
{
WriteRaw
(
stream
,
value
.
Length
);
WriteRaw
(
stream
,
value
.
Length
);
stream
.
Write
(
value
,
0
,
value
.
Length
);
stream
.
Write
(
value
,
0
,
value
.
Length
);
stream
.
Write
(
Crlf
,
0
,
2
);
stream
.
Write
(
Crlf
,
0
,
2
);
}
}
}
}
internal
void
WriteAsHex
(
byte
[]
value
)
internal
void
WriteAsHex
(
byte
[]
value
)
{
{
var
stream
=
outStream
;
var
stream
=
outStream
;
stream
.
WriteByte
((
byte
)
'$'
);
stream
.
WriteByte
((
byte
)
'$'
);
if
(
value
==
null
)
if
(
value
==
null
)
{
{
WriteRaw
(
stream
,
-
1
);
WriteRaw
(
stream
,
-
1
);
}
else
}
else
{
{
WriteRaw
(
stream
,
value
.
Length
*
2
);
WriteRaw
(
stream
,
value
.
Length
*
2
);
for
(
int
i
=
0
;
i
<
value
.
Length
;
i
++)
for
(
int
i
=
0
;
i
<
value
.
Length
;
i
++)
{
{
stream
.
WriteByte
(
ToHexNibble
(
value
[
i
]
>>
4
));
stream
.
WriteByte
(
ToHexNibble
(
value
[
i
]
>>
4
));
stream
.
WriteByte
(
ToHexNibble
(
value
[
i
]
&
15
));
stream
.
WriteByte
(
ToHexNibble
(
value
[
i
]
&
15
));
}
}
stream
.
Write
(
Crlf
,
0
,
2
);
stream
.
Write
(
Crlf
,
0
,
2
);
}
}
}
}
internal
static
byte
ToHexNibble
(
int
value
)
internal
static
byte
ToHexNibble
(
int
value
)
{
{
return
value
<
10
?
(
byte
)(
'0'
+
value
)
:
(
byte
)(
'a'
-
10
+
value
);
return
value
<
10
?
(
byte
)(
'0'
+
value
)
:
(
byte
)(
'a'
-
10
+
value
);
}
}
void
WriteUnified
(
Stream
stream
,
byte
[]
prefix
,
string
value
)
void
WriteUnified
(
Stream
stream
,
byte
[]
prefix
,
string
value
)
{
{
stream
.
WriteByte
((
byte
)
'$'
);
stream
.
WriteByte
((
byte
)
'$'
);
if
(
value
==
null
)
if
(
value
==
null
)
{
{
WriteRaw
(
stream
,
-
1
);
// note that not many things like this...
WriteRaw
(
stream
,
-
1
);
// note that not many things like this...
}
}
else
else
{
{
int
encodedLength
=
Encoding
.
UTF8
.
GetByteCount
(
value
);
int
encodedLength
=
Encoding
.
UTF8
.
GetByteCount
(
value
);
if
(
prefix
==
null
)
if
(
prefix
==
null
)
{
{
WriteRaw
(
stream
,
encodedLength
);
WriteRaw
(
stream
,
encodedLength
);
WriteRaw
(
stream
,
value
,
encodedLength
);
WriteRaw
(
stream
,
value
,
encodedLength
);
stream
.
Write
(
Crlf
,
0
,
2
);
stream
.
Write
(
Crlf
,
0
,
2
);
}
}
else
else
{
{
WriteRaw
(
stream
,
prefix
.
Length
+
encodedLength
);
WriteRaw
(
stream
,
prefix
.
Length
+
encodedLength
);
stream
.
Write
(
prefix
,
0
,
prefix
.
Length
);
stream
.
Write
(
prefix
,
0
,
prefix
.
Length
);
WriteRaw
(
stream
,
value
,
encodedLength
);
WriteRaw
(
stream
,
value
,
encodedLength
);
stream
.
Write
(
Crlf
,
0
,
2
);
stream
.
Write
(
Crlf
,
0
,
2
);
}
}
}
}
}
}
unsafe
void
WriteRaw
(
Stream
stream
,
string
value
,
int
encodedLength
)
unsafe
void
WriteRaw
(
Stream
stream
,
string
value
,
int
encodedLength
)
{
{
if
(
encodedLength
<=
ScratchSize
)
if
(
encodedLength
<=
ScratchSize
)
{
{
int
bytes
=
Encoding
.
UTF8
.
GetBytes
(
value
,
0
,
value
.
Length
,
outScratch
,
0
);
int
bytes
=
Encoding
.
UTF8
.
GetBytes
(
value
,
0
,
value
.
Length
,
outScratch
,
0
);
stream
.
Write
(
outScratch
,
0
,
bytes
);
stream
.
Write
(
outScratch
,
0
,
bytes
);
}
}
else
else
{
{
fixed
(
char
*
c
=
value
)
fixed
(
char
*
c
=
value
)
fixed
(
byte
*
b
=
outScratch
)
fixed
(
byte
*
b
=
outScratch
)
{
{
int
charsRemaining
=
value
.
Length
,
charOffset
=
0
,
bytesWritten
;
int
charsRemaining
=
value
.
Length
,
charOffset
=
0
,
bytesWritten
;
while
(
charsRemaining
>
Scratch_CharsPerBlock
)
while
(
charsRemaining
>
Scratch_CharsPerBlock
)
{
{
bytesWritten
=
outEncoder
.
GetBytes
(
c
+
charOffset
,
Scratch_CharsPerBlock
,
b
,
ScratchSize
,
false
);
bytesWritten
=
outEncoder
.
GetBytes
(
c
+
charOffset
,
Scratch_CharsPerBlock
,
b
,
ScratchSize
,
false
);
stream
.
Write
(
outScratch
,
0
,
bytesWritten
);
stream
.
Write
(
outScratch
,
0
,
bytesWritten
);
charOffset
+=
Scratch_CharsPerBlock
;
charOffset
+=
Scratch_CharsPerBlock
;
charsRemaining
-=
Scratch_CharsPerBlock
;
charsRemaining
-=
Scratch_CharsPerBlock
;
}
}
bytesWritten
=
outEncoder
.
GetBytes
(
c
+
charOffset
,
charsRemaining
,
b
,
ScratchSize
,
true
);
bytesWritten
=
outEncoder
.
GetBytes
(
c
+
charOffset
,
charsRemaining
,
b
,
ScratchSize
,
true
);
if
(
bytesWritten
!=
0
)
stream
.
Write
(
outScratch
,
0
,
bytesWritten
);
if
(
bytesWritten
!=
0
)
stream
.
Write
(
outScratch
,
0
,
bytesWritten
);
}
}
}
}
}
}
const
int
ScratchSize
=
512
;
const
int
ScratchSize
=
512
;
static
readonly
int
Scratch_CharsPerBlock
=
ScratchSize
/
Encoding
.
UTF8
.
GetMaxByteCount
(
1
);
static
readonly
int
Scratch_CharsPerBlock
=
ScratchSize
/
Encoding
.
UTF8
.
GetMaxByteCount
(
1
);
private
readonly
byte
[]
outScratch
=
new
byte
[
ScratchSize
];
private
readonly
byte
[]
outScratch
=
new
byte
[
ScratchSize
];
private
readonly
Encoder
outEncoder
=
Encoding
.
UTF8
.
GetEncoder
();
private
readonly
Encoder
outEncoder
=
Encoding
.
UTF8
.
GetEncoder
();
static
void
WriteUnified
(
Stream
stream
,
byte
[]
prefix
,
byte
[]
value
)
static
void
WriteUnified
(
Stream
stream
,
byte
[]
prefix
,
byte
[]
value
)
{
{
stream
.
WriteByte
((
byte
)
'$'
);
stream
.
WriteByte
((
byte
)
'$'
);
if
(
value
==
null
)
if
(
value
==
null
)
{
{
WriteRaw
(
stream
,
-
1
);
// note that not many things like this...
WriteRaw
(
stream
,
-
1
);
// note that not many things like this...
}
}
else
if
(
prefix
==
null
)
else
if
(
prefix
==
null
)
{
{
WriteRaw
(
stream
,
value
.
Length
);
WriteRaw
(
stream
,
value
.
Length
);
stream
.
Write
(
value
,
0
,
value
.
Length
);
stream
.
Write
(
value
,
0
,
value
.
Length
);
stream
.
Write
(
Crlf
,
0
,
2
);
stream
.
Write
(
Crlf
,
0
,
2
);
}
}
else
else
{
{
WriteRaw
(
stream
,
prefix
.
Length
+
value
.
Length
);
WriteRaw
(
stream
,
prefix
.
Length
+
value
.
Length
);
stream
.
Write
(
prefix
,
0
,
prefix
.
Length
);
stream
.
Write
(
prefix
,
0
,
prefix
.
Length
);
stream
.
Write
(
value
,
0
,
value
.
Length
);
stream
.
Write
(
value
,
0
,
value
.
Length
);
stream
.
Write
(
Crlf
,
0
,
2
);
stream
.
Write
(
Crlf
,
0
,
2
);
}
}
}
}
static
void
WriteUnified
(
Stream
stream
,
long
value
)
static
void
WriteUnified
(
Stream
stream
,
long
value
)
{
{
// note from specification: A client sends to the Redis server a RESP Array consisting of just Bulk Strings.
// note from specification: A client sends to the Redis server a RESP Array consisting of just Bulk Strings.
// (i.e. we can't just send ":123\r\n", we need to send "$3\r\n123\r\n"
// (i.e. we can't just send ":123\r\n", we need to send "$3\r\n123\r\n"
stream
.
WriteByte
((
byte
)
'$'
);
stream
.
WriteByte
((
byte
)
'$'
);
WriteRaw
(
stream
,
value
,
withLengthPrefix
:
true
);
WriteRaw
(
stream
,
value
,
withLengthPrefix
:
true
);
}
}
void
BeginReading
()
void
BeginReading
()
{
{
bool
keepReading
;
bool
keepReading
;
try
try
{
{
do
do
{
{
keepReading
=
false
;
keepReading
=
false
;
int
space
=
EnsureSpaceAndComputeBytesToRead
();
int
space
=
EnsureSpaceAndComputeBytesToRead
();
multiplexer
.
Trace
(
"Beginning async read..."
,
physicalName
);
multiplexer
.
Trace
(
"Beginning async read..."
,
physicalName
);
var
result
=
netStream
.
BeginRead
(
ioBuffer
,
ioBufferBytes
,
space
,
endRead
,
this
);
var
result
=
netStream
.
BeginRead
(
ioBuffer
,
ioBufferBytes
,
space
,
endRead
,
this
);
if
(
result
.
CompletedSynchronously
)
if
(
result
.
CompletedSynchronously
)
{
{
multiplexer
.
Trace
(
"Completed synchronously: processing immediately"
,
physicalName
);
multiplexer
.
Trace
(
"Completed synchronously: processing immediately"
,
physicalName
);
keepReading
=
EndReading
(
result
);
keepReading
=
EndReading
(
result
);
}
}
}
while
(
keepReading
);
}
while
(
keepReading
);
}
}
catch
(
System
.
IO
.
IOException
ex
)
catch
(
System
.
IO
.
IOException
ex
)
{
{
multiplexer
.
Trace
(
"Could not connect: "
+
ex
.
Message
,
physicalName
);
multiplexer
.
Trace
(
"Could not connect: "
+
ex
.
Message
,
physicalName
);
}
}
}
}
int
haveReader
;
int
haveReader
;
internal
int
GetAvailableInboundBytes
(
out
int
activeReaders
)
internal
int
GetAvailableInboundBytes
(
out
int
activeReaders
)
{
{
activeReaders
=
Interlocked
.
CompareExchange
(
ref
haveReader
,
0
,
0
);
activeReaders
=
Interlocked
.
CompareExchange
(
ref
haveReader
,
0
,
0
);
return
this
.
socketToken
.
Available
;
return
this
.
socketToken
.
Available
;
}
}
static
LocalCertificateSelectionCallback
GetAmbientCertificateCallback
()
static
LocalCertificateSelectionCallback
GetAmbientCertificateCallback
()
...
@@ -646,357 +646,357 @@ static LocalCertificateSelectionCallback GetAmbientCertificateCallback()
...
@@ -646,357 +646,357 @@ static LocalCertificateSelectionCallback GetAmbientCertificateCallback()
}
catch
}
catch
{
}
{
}
return
null
;
return
null
;
}
}
SocketMode
ISocketCallback
.
Connected
(
Stream
stream
)
SocketMode
ISocketCallback
.
Connected
(
Stream
stream
)
{
{
try
try
{
{
var
socketMode
=
SocketManager
.
DefaultSocketMode
;
var
socketMode
=
SocketManager
.
DefaultSocketMode
;
// disallow connection in some cases
// disallow connection in some cases
OnDebugAbort
();
OnDebugAbort
();
// the order is important here:
// the order is important here:
// [network]<==[ssl]<==[logging]<==[buffered]
// [network]<==[ssl]<==[logging]<==[buffered]
var
config
=
multiplexer
.
RawConfig
;
var
config
=
multiplexer
.
RawConfig
;
if
(
config
.
Ssl
)
if
(
config
.
Ssl
)
{
{
var
host
=
config
.
SslHost
;
var
host
=
config
.
SslHost
;
if
(
string
.
IsNullOrWhiteSpace
(
host
))
host
=
Format
.
ToStringHostOnly
(
bridge
.
ServerEndPoint
.
EndPoint
);
if
(
string
.
IsNullOrWhiteSpace
(
host
))
host
=
Format
.
ToStringHostOnly
(
bridge
.
ServerEndPoint
.
EndPoint
);
var
ssl
=
new
SslStream
(
stream
,
false
,
config
.
CertificateValidationCallback
,
var
ssl
=
new
SslStream
(
stream
,
false
,
config
.
CertificateValidationCallback
,
config
.
CertificateSelectionCallback
??
GetAmbientCertificateCallback
()
config
.
CertificateSelectionCallback
??
GetAmbientCertificateCallback
()
#if !__MonoCS__
#if !__MonoCS__
,
EncryptionPolicy
.
RequireEncryption
,
EncryptionPolicy
.
RequireEncryption
#endif
#endif
);
);
ssl
.
AuthenticateAsClient
(
host
);
ssl
.
AuthenticateAsClient
(
host
);
if
(!
ssl
.
IsEncrypted
)
if
(!
ssl
.
IsEncrypted
)
{
{
RecordConnectionFailed
(
ConnectionFailureType
.
AuthenticationFailure
);
RecordConnectionFailed
(
ConnectionFailureType
.
AuthenticationFailure
);
multiplexer
.
Trace
(
"Encryption failure"
);
multiplexer
.
Trace
(
"Encryption failure"
);
return
SocketMode
.
Abort
;
return
SocketMode
.
Abort
;
}
}
stream
=
ssl
;
stream
=
ssl
;
socketMode
=
SocketMode
.
Async
;
socketMode
=
SocketMode
.
Async
;
}
}
OnWrapForLogging
(
ref
stream
,
physicalName
);
OnWrapForLogging
(
ref
stream
,
physicalName
);
int
bufferSize
=
config
.
WriteBuffer
;
int
bufferSize
=
config
.
WriteBuffer
;
this
.
netStream
=
stream
;
this
.
netStream
=
stream
;
this
.
outStream
=
bufferSize
<=
0
?
stream
:
new
BufferedStream
(
stream
,
bufferSize
);
this
.
outStream
=
bufferSize
<=
0
?
stream
:
new
BufferedStream
(
stream
,
bufferSize
);
multiplexer
.
Trace
(
"Connected"
,
physicalName
);
multiplexer
.
Trace
(
"Connected"
,
physicalName
);
bridge
.
OnConnected
(
this
);
bridge
.
OnConnected
(
this
);
return
socketMode
;
return
socketMode
;
}
}
catch
(
Exception
ex
)
catch
(
Exception
ex
)
{
{
RecordConnectionFailed
(
ConnectionFailureType
.
InternalFailure
,
ex
);
// includes a bridge.OnDisconnected
RecordConnectionFailed
(
ConnectionFailureType
.
InternalFailure
,
ex
);
// includes a bridge.OnDisconnected
multiplexer
.
Trace
(
"Could not connect: "
+
ex
.
Message
,
physicalName
);
multiplexer
.
Trace
(
"Could not connect: "
+
ex
.
Message
,
physicalName
);
return
SocketMode
.
Abort
;
return
SocketMode
.
Abort
;
}
}
}
}
private
bool
EndReading
(
IAsyncResult
result
)
private
bool
EndReading
(
IAsyncResult
result
)
{
{
try
try
{
{
var
tmp
=
netStream
;
var
tmp
=
netStream
;
int
bytesRead
=
tmp
==
null
?
0
:
tmp
.
EndRead
(
result
);
int
bytesRead
=
tmp
==
null
?
0
:
tmp
.
EndRead
(
result
);
return
ProcessReadBytes
(
bytesRead
);
return
ProcessReadBytes
(
bytesRead
);
}
}
catch
(
Exception
ex
)
catch
(
Exception
ex
)
{
{
RecordConnectionFailed
(
ConnectionFailureType
.
InternalFailure
,
ex
);
RecordConnectionFailed
(
ConnectionFailureType
.
InternalFailure
,
ex
);
return
false
;
return
false
;
}
}
}
}
int
EnsureSpaceAndComputeBytesToRead
()
int
EnsureSpaceAndComputeBytesToRead
()
{
{
int
space
=
ioBuffer
.
Length
-
ioBufferBytes
;
int
space
=
ioBuffer
.
Length
-
ioBufferBytes
;
if
(
space
==
0
)
if
(
space
==
0
)
{
{
Array
.
Resize
(
ref
ioBuffer
,
ioBuffer
.
Length
*
2
);
Array
.
Resize
(
ref
ioBuffer
,
ioBuffer
.
Length
*
2
);
space
=
ioBuffer
.
Length
-
ioBufferBytes
;
space
=
ioBuffer
.
Length
-
ioBufferBytes
;
}
}
return
space
;
return
space
;
}
}
void
ISocketCallback
.
Error
()
void
ISocketCallback
.
Error
()
{
{
RecordConnectionFailed
(
ConnectionFailureType
.
SocketFailure
);
RecordConnectionFailed
(
ConnectionFailureType
.
SocketFailure
);
}
}
void
MatchResult
(
RawResult
result
)
void
MatchResult
(
RawResult
result
)
{
{
// check to see if it could be an out-of-band pubsub message
// check to see if it could be an out-of-band pubsub message
if
(
connectionType
==
ConnectionType
.
Subscription
&&
result
.
Type
==
ResultType
.
MultiBulk
)
if
(
connectionType
==
ConnectionType
.
Subscription
&&
result
.
Type
==
ResultType
.
MultiBulk
)
{
// out of band message does not match to a queued message
{
// out of band message does not match to a queued message
var
items
=
result
.
GetItems
();
var
items
=
result
.
GetItems
();
if
(
items
.
Length
>=
3
&&
items
[
0
].
IsEqual
(
message
))
if
(
items
.
Length
>=
3
&&
items
[
0
].
IsEqual
(
message
))
{
{
// special-case the configuration change broadcasts (we don't keep that in the usual pub/sub registry)
// special-case the configuration change broadcasts (we don't keep that in the usual pub/sub registry)
var
configChanged
=
multiplexer
.
ConfigurationChangedChannel
;
var
configChanged
=
multiplexer
.
ConfigurationChangedChannel
;
if
(
configChanged
!=
null
&&
items
[
1
].
IsEqual
(
configChanged
))
if
(
configChanged
!=
null
&&
items
[
1
].
IsEqual
(
configChanged
))
{
{
EndPoint
blame
=
null
;
EndPoint
blame
=
null
;
try
try
{
{
if
(!
items
[
2
].
IsEqual
(
RedisLiterals
.
ByteWildcard
))
if
(!
items
[
2
].
IsEqual
(
RedisLiterals
.
ByteWildcard
))
{
{
blame
=
Format
.
TryParseEndPoint
(
items
[
2
].
GetString
());
blame
=
Format
.
TryParseEndPoint
(
items
[
2
].
GetString
());
}
}
}
}
catch
{
/* no biggie */
}
catch
{
/* no biggie */
}
multiplexer
.
Trace
(
"Configuration changed: "
+
Format
.
ToString
(
blame
),
physicalName
);
multiplexer
.
Trace
(
"Configuration changed: "
+
Format
.
ToString
(
blame
),
physicalName
);
multiplexer
.
ReconfigureIfNeeded
(
blame
,
true
,
"broadcast"
);
multiplexer
.
ReconfigureIfNeeded
(
blame
,
true
,
"broadcast"
);
}
}
// invoke the handlers
// invoke the handlers
var
channel
=
items
[
1
].
AsRedisChannel
(
ChannelPrefix
,
RedisChannel
.
PatternMode
.
Literal
);
var
channel
=
items
[
1
].
AsRedisChannel
(
ChannelPrefix
,
RedisChannel
.
PatternMode
.
Literal
);
multiplexer
.
Trace
(
"MESSAGE: "
+
channel
,
physicalName
);
multiplexer
.
Trace
(
"MESSAGE: "
+
channel
,
physicalName
);
if
(!
channel
.
IsNull
)
if
(!
channel
.
IsNull
)
{
{
multiplexer
.
OnMessage
(
channel
,
channel
,
items
[
2
].
AsRedisValue
());
multiplexer
.
OnMessage
(
channel
,
channel
,
items
[
2
].
AsRedisValue
());
}
}
return
;
// AND STOP PROCESSING!
return
;
// AND STOP PROCESSING!
}
}
else
if
(
items
.
Length
>=
4
&&
items
[
0
].
IsEqual
(
pmessage
))
else
if
(
items
.
Length
>=
4
&&
items
[
0
].
IsEqual
(
pmessage
))
{
{
var
channel
=
items
[
2
].
AsRedisChannel
(
ChannelPrefix
,
RedisChannel
.
PatternMode
.
Literal
);
var
channel
=
items
[
2
].
AsRedisChannel
(
ChannelPrefix
,
RedisChannel
.
PatternMode
.
Literal
);
multiplexer
.
Trace
(
"PMESSAGE: "
+
channel
,
physicalName
);
multiplexer
.
Trace
(
"PMESSAGE: "
+
channel
,
physicalName
);
if
(!
channel
.
IsNull
)
if
(!
channel
.
IsNull
)
{
{
var
sub
=
items
[
1
].
AsRedisChannel
(
ChannelPrefix
,
RedisChannel
.
PatternMode
.
Pattern
);
var
sub
=
items
[
1
].
AsRedisChannel
(
ChannelPrefix
,
RedisChannel
.
PatternMode
.
Pattern
);
multiplexer
.
OnMessage
(
sub
,
channel
,
items
[
3
].
AsRedisValue
());
multiplexer
.
OnMessage
(
sub
,
channel
,
items
[
3
].
AsRedisValue
());
}
}
return
;
// AND STOP PROCESSING!
return
;
// AND STOP PROCESSING!
}
}
// if it didn't look like "[p]message", then we still need to process the pending queue
// if it didn't look like "[p]message", then we still need to process the pending queue
}
}
multiplexer
.
Trace
(
"Matching result..."
,
physicalName
);
multiplexer
.
Trace
(
"Matching result..."
,
physicalName
);
Message
msg
;
Message
msg
;
lock
(
outstanding
)
lock
(
outstanding
)
{
{
multiplexer
.
Trace
(
outstanding
.
Count
==
0
,
"Nothing to respond to!"
,
physicalName
);
multiplexer
.
Trace
(
outstanding
.
Count
==
0
,
"Nothing to respond to!"
,
physicalName
);
msg
=
outstanding
.
Dequeue
();
msg
=
outstanding
.
Dequeue
();
}
}
multiplexer
.
Trace
(
"Response to: "
+
msg
.
ToString
(),
physicalName
);
multiplexer
.
Trace
(
"Response to: "
+
msg
.
ToString
(),
physicalName
);
if
(
msg
.
ComputeResult
(
this
,
result
))
if
(
msg
.
ComputeResult
(
this
,
result
))
{
{
bridge
.
CompleteSyncOrAsync
(
msg
);
bridge
.
CompleteSyncOrAsync
(
msg
);
}
}
}
}
partial
void
OnCloseEcho
();
partial
void
OnCloseEcho
();
partial
void
OnCreateEcho
();
partial
void
OnCreateEcho
();
partial
void
OnDebugAbort
();
partial
void
OnDebugAbort
();
void
ISocketCallback
.
OnHeartbeat
()
void
ISocketCallback
.
OnHeartbeat
()
{
{
try
try
{
{
bridge
.
OnHeartbeat
(
true
);
// all the fun code is here
bridge
.
OnHeartbeat
(
true
);
// all the fun code is here
}
}
catch
(
Exception
ex
)
catch
(
Exception
ex
)
{
{
OnInternalError
(
ex
);
OnInternalError
(
ex
);
}
}
}
}
partial
void
OnWrapForLogging
(
ref
Stream
stream
,
string
name
);
partial
void
OnWrapForLogging
(
ref
Stream
stream
,
string
name
);
private
int
ProcessBuffer
(
byte
[]
underlying
,
ref
int
offset
,
ref
int
count
)
private
int
ProcessBuffer
(
byte
[]
underlying
,
ref
int
offset
,
ref
int
count
)
{
{
int
messageCount
=
0
;
int
messageCount
=
0
;
RawResult
result
;
RawResult
result
;
do
do
{
{
int
tmpOffset
=
offset
,
tmpCount
=
count
;
int
tmpOffset
=
offset
,
tmpCount
=
count
;
// we want TryParseResult to be able to mess with these without consequence
// we want TryParseResult to be able to mess with these without consequence
result
=
TryParseResult
(
underlying
,
ref
tmpOffset
,
ref
tmpCount
);
result
=
TryParseResult
(
underlying
,
ref
tmpOffset
,
ref
tmpCount
);
if
(
result
.
HasValue
)
if
(
result
.
HasValue
)
{
{
messageCount
++;
messageCount
++;
// entire message: update the external counters
// entire message: update the external counters
offset
=
tmpOffset
;
offset
=
tmpOffset
;
count
=
tmpCount
;
count
=
tmpCount
;
multiplexer
.
Trace
(
result
.
ToString
(),
physicalName
);
multiplexer
.
Trace
(
result
.
ToString
(),
physicalName
);
MatchResult
(
result
);
MatchResult
(
result
);
}
}
}
while
(
result
.
HasValue
);
}
while
(
result
.
HasValue
);
return
messageCount
;
return
messageCount
;
}
}
private
bool
ProcessReadBytes
(
int
bytesRead
)
private
bool
ProcessReadBytes
(
int
bytesRead
)
{
{
if
(
bytesRead
<=
0
)
if
(
bytesRead
<=
0
)
{
{
multiplexer
.
Trace
(
"EOF"
,
physicalName
);
multiplexer
.
Trace
(
"EOF"
,
physicalName
);
RecordConnectionFailed
(
ConnectionFailureType
.
SocketClosed
);
RecordConnectionFailed
(
ConnectionFailureType
.
SocketClosed
);
return
false
;
return
false
;
}
}
Interlocked
.
Exchange
(
ref
lastReadTickCount
,
Environment
.
TickCount
);
Interlocked
.
Exchange
(
ref
lastReadTickCount
,
Environment
.
TickCount
);
// reset unanswered write timestamp
// reset unanswered write timestamp
Thread
.
VolatileWrite
(
ref
firstUnansweredWriteTickCount
,
0
);
Thread
.
VolatileWrite
(
ref
firstUnansweredWriteTickCount
,
0
);
ioBufferBytes
+=
bytesRead
;
ioBufferBytes
+=
bytesRead
;
multiplexer
.
Trace
(
"More bytes available: "
+
bytesRead
+
" ("
+
ioBufferBytes
+
")"
,
physicalName
);
multiplexer
.
Trace
(
"More bytes available: "
+
bytesRead
+
" ("
+
ioBufferBytes
+
")"
,
physicalName
);
int
offset
=
0
,
count
=
ioBufferBytes
;
int
offset
=
0
,
count
=
ioBufferBytes
;
int
handled
=
ProcessBuffer
(
ioBuffer
,
ref
offset
,
ref
count
);
int
handled
=
ProcessBuffer
(
ioBuffer
,
ref
offset
,
ref
count
);
multiplexer
.
Trace
(
"Processed: "
+
handled
,
physicalName
);
multiplexer
.
Trace
(
"Processed: "
+
handled
,
physicalName
);
if
(
handled
!=
0
)
if
(
handled
!=
0
)
{
{
// read stuff
// read stuff
if
(
count
!=
0
)
if
(
count
!=
0
)
{
{
multiplexer
.
Trace
(
"Copying remaining bytes: "
+
count
,
physicalName
);
multiplexer
.
Trace
(
"Copying remaining bytes: "
+
count
,
physicalName
);
// if anything was left over, we need to copy it to
// if anything was left over, we need to copy it to
// the start of the buffer so it can be used next time
// the start of the buffer so it can be used next time
Buffer
.
BlockCopy
(
ioBuffer
,
offset
,
ioBuffer
,
0
,
count
);
Buffer
.
BlockCopy
(
ioBuffer
,
offset
,
ioBuffer
,
0
,
count
);
}
}
ioBufferBytes
=
count
;
ioBufferBytes
=
count
;
}
}
return
true
;
return
true
;
}
}
void
ISocketCallback
.
Read
()
void
ISocketCallback
.
Read
()
{
{
Interlocked
.
Increment
(
ref
haveReader
);
Interlocked
.
Increment
(
ref
haveReader
);
try
try
{
{
do
do
{
{
int
space
=
EnsureSpaceAndComputeBytesToRead
();
int
space
=
EnsureSpaceAndComputeBytesToRead
();
var
tmp
=
netStream
;
var
tmp
=
netStream
;
int
bytesRead
=
tmp
==
null
?
0
:
tmp
.
Read
(
ioBuffer
,
ioBufferBytes
,
space
);
int
bytesRead
=
tmp
==
null
?
0
:
tmp
.
Read
(
ioBuffer
,
ioBufferBytes
,
space
);
if
(!
ProcessReadBytes
(
bytesRead
))
return
;
// EOF
if
(!
ProcessReadBytes
(
bytesRead
))
return
;
// EOF
}
while
(
socketToken
.
Available
!=
0
);
}
while
(
socketToken
.
Available
!=
0
);
multiplexer
.
Trace
(
"Buffer exhausted"
,
physicalName
);
multiplexer
.
Trace
(
"Buffer exhausted"
,
physicalName
);
// ^^^ note that the socket manager will call us again when there is something to do
// ^^^ note that the socket manager will call us again when there is something to do
}
}
catch
(
Exception
ex
)
catch
(
Exception
ex
)
{
{
RecordConnectionFailed
(
ConnectionFailureType
.
InternalFailure
,
ex
);
RecordConnectionFailed
(
ConnectionFailureType
.
InternalFailure
,
ex
);
}
finally
}
finally
{
{
Interlocked
.
Decrement
(
ref
haveReader
);
Interlocked
.
Decrement
(
ref
haveReader
);
}
}
}
}
private
RawResult
ReadArray
(
byte
[]
buffer
,
ref
int
offset
,
ref
int
count
)
private
RawResult
ReadArray
(
byte
[]
buffer
,
ref
int
offset
,
ref
int
count
)
{
{
var
itemCount
=
ReadLineTerminatedString
(
ResultType
.
Integer
,
buffer
,
ref
offset
,
ref
count
);
var
itemCount
=
ReadLineTerminatedString
(
ResultType
.
Integer
,
buffer
,
ref
offset
,
ref
count
);
if
(
itemCount
.
HasValue
)
if
(
itemCount
.
HasValue
)
{
{
long
i64
;
long
i64
;
if
(!
itemCount
.
TryGetInt64
(
out
i64
))
throw
ExceptionFactory
.
ConnectionFailure
(
multiplexer
.
IncludeDetailInExceptions
,
ConnectionFailureType
.
ProtocolFailure
,
"Invalid array length"
,
bridge
.
ServerEndPoint
);
if
(!
itemCount
.
TryGetInt64
(
out
i64
))
throw
ExceptionFactory
.
ConnectionFailure
(
multiplexer
.
IncludeDetailInExceptions
,
ConnectionFailureType
.
ProtocolFailure
,
"Invalid array length"
,
bridge
.
ServerEndPoint
);
int
itemCountActual
=
checked
((
int
)
i64
);
int
itemCountActual
=
checked
((
int
)
i64
);
if
(
itemCountActual
<=
0
)
return
RawResult
.
EmptyArray
;
if
(
itemCountActual
<=
0
)
return
RawResult
.
EmptyArray
;
var
arr
=
new
RawResult
[
itemCountActual
];
var
arr
=
new
RawResult
[
itemCountActual
];
for
(
int
i
=
0
;
i
<
itemCountActual
;
i
++)
for
(
int
i
=
0
;
i
<
itemCountActual
;
i
++)
{
{
if
(!(
arr
[
i
]
=
TryParseResult
(
buffer
,
ref
offset
,
ref
count
)).
HasValue
)
if
(!(
arr
[
i
]
=
TryParseResult
(
buffer
,
ref
offset
,
ref
count
)).
HasValue
)
return
RawResult
.
Nil
;
return
RawResult
.
Nil
;
}
}
return
new
RawResult
(
arr
);
return
new
RawResult
(
arr
);
}
}
return
RawResult
.
Nil
;
return
RawResult
.
Nil
;
}
}
private
RawResult
ReadBulkString
(
byte
[]
buffer
,
ref
int
offset
,
ref
int
count
)
private
RawResult
ReadBulkString
(
byte
[]
buffer
,
ref
int
offset
,
ref
int
count
)
{
{
var
prefix
=
ReadLineTerminatedString
(
ResultType
.
Integer
,
buffer
,
ref
offset
,
ref
count
);
var
prefix
=
ReadLineTerminatedString
(
ResultType
.
Integer
,
buffer
,
ref
offset
,
ref
count
);
if
(
prefix
.
HasValue
)
if
(
prefix
.
HasValue
)
{
{
long
i64
;
long
i64
;
if
(!
prefix
.
TryGetInt64
(
out
i64
))
throw
ExceptionFactory
.
ConnectionFailure
(
multiplexer
.
IncludeDetailInExceptions
,
ConnectionFailureType
.
ProtocolFailure
,
"Invalid bulk string length"
,
bridge
.
ServerEndPoint
);
if
(!
prefix
.
TryGetInt64
(
out
i64
))
throw
ExceptionFactory
.
ConnectionFailure
(
multiplexer
.
IncludeDetailInExceptions
,
ConnectionFailureType
.
ProtocolFailure
,
"Invalid bulk string length"
,
bridge
.
ServerEndPoint
);
int
bodySize
=
checked
((
int
)
i64
);
int
bodySize
=
checked
((
int
)
i64
);
if
(
bodySize
<
0
)
if
(
bodySize
<
0
)
{
{
return
new
RawResult
(
ResultType
.
BulkString
,
null
,
0
,
0
);
return
new
RawResult
(
ResultType
.
BulkString
,
null
,
0
,
0
);
}
}
else
if
(
count
>=
bodySize
+
2
)
else
if
(
count
>=
bodySize
+
2
)
{
{
if
(
buffer
[
offset
+
bodySize
]
!=
'\r'
||
buffer
[
offset
+
bodySize
+
1
]
!=
'\n'
)
if
(
buffer
[
offset
+
bodySize
]
!=
'\r'
||
buffer
[
offset
+
bodySize
+
1
]
!=
'\n'
)
{
{
throw
ExceptionFactory
.
ConnectionFailure
(
multiplexer
.
IncludeDetailInExceptions
,
ConnectionFailureType
.
ProtocolFailure
,
"Invalid bulk string terminator"
,
bridge
.
ServerEndPoint
);
throw
ExceptionFactory
.
ConnectionFailure
(
multiplexer
.
IncludeDetailInExceptions
,
ConnectionFailureType
.
ProtocolFailure
,
"Invalid bulk string terminator"
,
bridge
.
ServerEndPoint
);
}
}
var
result
=
new
RawResult
(
ResultType
.
BulkString
,
buffer
,
offset
,
bodySize
);
var
result
=
new
RawResult
(
ResultType
.
BulkString
,
buffer
,
offset
,
bodySize
);
offset
+=
bodySize
+
2
;
offset
+=
bodySize
+
2
;
count
-=
bodySize
+
2
;
count
-=
bodySize
+
2
;
return
result
;
return
result
;
}
}
}
}
return
RawResult
.
Nil
;
return
RawResult
.
Nil
;
}
}
private
RawResult
ReadLineTerminatedString
(
ResultType
type
,
byte
[]
buffer
,
ref
int
offset
,
ref
int
count
)
private
RawResult
ReadLineTerminatedString
(
ResultType
type
,
byte
[]
buffer
,
ref
int
offset
,
ref
int
count
)
{
{
int
max
=
offset
+
count
-
2
;
int
max
=
offset
+
count
-
2
;
for
(
int
i
=
offset
;
i
<
max
;
i
++)
for
(
int
i
=
offset
;
i
<
max
;
i
++)
{
{
if
(
buffer
[
i
+
1
]
==
'\r'
&&
buffer
[
i
+
2
]
==
'\n'
)
if
(
buffer
[
i
+
1
]
==
'\r'
&&
buffer
[
i
+
2
]
==
'\n'
)
{
{
int
len
=
i
-
offset
+
1
;
int
len
=
i
-
offset
+
1
;
var
result
=
new
RawResult
(
type
,
buffer
,
offset
,
len
);
var
result
=
new
RawResult
(
type
,
buffer
,
offset
,
len
);
count
-=
(
len
+
2
);
count
-=
(
len
+
2
);
offset
+=
(
len
+
2
);
offset
+=
(
len
+
2
);
return
result
;
return
result
;
}
}
}
}
return
RawResult
.
Nil
;
return
RawResult
.
Nil
;
}
}
void
ISocketCallback
.
StartReading
()
void
ISocketCallback
.
StartReading
()
{
{
BeginReading
();
BeginReading
();
}
}
RawResult
TryParseResult
(
byte
[]
buffer
,
ref
int
offset
,
ref
int
count
)
RawResult
TryParseResult
(
byte
[]
buffer
,
ref
int
offset
,
ref
int
count
)
{
{
if
(
count
==
0
)
return
RawResult
.
Nil
;
if
(
count
==
0
)
return
RawResult
.
Nil
;
char
resultType
=
(
char
)
buffer
[
offset
++];
char
resultType
=
(
char
)
buffer
[
offset
++];
count
--;
count
--;
switch
(
resultType
)
switch
(
resultType
)
{
{
case
'+'
:
// simple string
case
'+'
:
// simple string
return
ReadLineTerminatedString
(
ResultType
.
SimpleString
,
buffer
,
ref
offset
,
ref
count
);
return
ReadLineTerminatedString
(
ResultType
.
SimpleString
,
buffer
,
ref
offset
,
ref
count
);
case
'-'
:
// error
case
'-'
:
// error
return
ReadLineTerminatedString
(
ResultType
.
Error
,
buffer
,
ref
offset
,
ref
count
);
return
ReadLineTerminatedString
(
ResultType
.
Error
,
buffer
,
ref
offset
,
ref
count
);
case
':'
:
// integer
case
':'
:
// integer
return
ReadLineTerminatedString
(
ResultType
.
Integer
,
buffer
,
ref
offset
,
ref
count
);
return
ReadLineTerminatedString
(
ResultType
.
Integer
,
buffer
,
ref
offset
,
ref
count
);
case
'$'
:
// bulk string
case
'$'
:
// bulk string
return
ReadBulkString
(
buffer
,
ref
offset
,
ref
count
);
return
ReadBulkString
(
buffer
,
ref
offset
,
ref
count
);
case
'*'
:
// array
case
'*'
:
// array
return
ReadArray
(
buffer
,
ref
offset
,
ref
count
);
return
ReadArray
(
buffer
,
ref
offset
,
ref
count
);
default
:
default
:
throw
new
InvalidOperationException
(
"Unexpected response prefix: "
+
(
char
)
resultType
);
throw
new
InvalidOperationException
(
"Unexpected response prefix: "
+
(
char
)
resultType
);
}
}
}
}
partial
void
DebugEmulateStaleConnection
(
ref
int
firstUnansweredWrite
);
partial
void
DebugEmulateStaleConnection
(
ref
int
firstUnansweredWrite
);
public
void
CheckForStaleConnection
()
public
void
CheckForStaleConnection
()
{
{
int
firstUnansweredWrite
;
int
firstUnansweredWrite
;
firstUnansweredWrite
=
Thread
.
VolatileRead
(
ref
firstUnansweredWriteTickCount
);
firstUnansweredWrite
=
Thread
.
VolatileRead
(
ref
firstUnansweredWriteTickCount
);
DebugEmulateStaleConnection
(
ref
firstUnansweredWrite
);
DebugEmulateStaleConnection
(
ref
firstUnansweredWrite
);
int
now
=
Environment
.
TickCount
;
int
now
=
Environment
.
TickCount
;
if
(
firstUnansweredWrite
!=
0
&&
(
now
-
firstUnansweredWrite
)
>
this
.
multiplexer
.
RawConfig
.
SyncTimeout
)
if
(
firstUnansweredWrite
!=
0
&&
(
now
-
firstUnansweredWrite
)
>
this
.
multiplexer
.
RawConfig
.
SyncTimeout
)
{
{
this
.
RecordConnectionFailed
(
ConnectionFailureType
.
SocketFailure
,
origin
:
"CheckForStaleConnection"
);
this
.
RecordConnectionFailed
(
ConnectionFailureType
.
SocketFailure
,
origin
:
"CheckForStaleConnection"
);
}
}
}
}
}
}
}
}
StackExchange.Redis/StackExchange/Redis/RedisDatabase.cs
View file @
3aebb0df
This source diff could not be displayed because it is too large. You can
view the blob
instead.
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