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
4e5bbd11
Commit
4e5bbd11
authored
Jul 01, 2018
by
Nick Craver
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Documentation cleanup
parent
5b5be771
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
117 additions
and
96 deletions
+117
-96
Attributes.cs
StackExchange.Redis.Tests/Helpers/Attributes.cs
+6
-4
ConcurrentProfileStorageCollection.cs
...StackExchange/Redis/ConcurrentProfileStorageCollection.cs
+25
-25
ConnectionMultiplexer.Profiling.cs
...is/StackExchange/Redis/ConnectionMultiplexer.Profiling.cs
+10
-11
RetransmissionReasonType.cs
...dis/StackExchange/Redis/Enums/RetransmissionReasonType.cs
+4
-1
IProfiler.cs
...xchange.Redis/StackExchange/Redis/Interfaces/IProfiler.cs
+3
-2
LuaScript.cs
StackExchange.Redis/StackExchange/Redis/LuaScript.cs
+32
-26
ProfileContextTracker.cs
...change.Redis/StackExchange/Redis/ProfileContextTracker.cs
+22
-19
RedisKey.cs
StackExchange.Redis/StackExchange/Redis/RedisKey.cs
+6
-4
RedisValue.cs
StackExchange.Redis/StackExchange/Redis/RedisValue.cs
+2
-0
ScriptParameterMapper.cs
...change.Redis/StackExchange/Redis/ScriptParameterMapper.cs
+7
-4
No files found.
StackExchange.Redis.Tests/Helpers/Attributes.cs
View file @
4e5bbd11
...
...
@@ -9,11 +9,12 @@
namespace
StackExchange.Redis.Tests
{
/// <summary>
///
Override for <see cref="Xunit.FactAttribute"/> that truncates our DisplayName down.
///
///
<para>Override for <see cref="Xunit.FactAttribute"/> that truncates our DisplayName down.</para>
///
<para>
/// Attribute that is applied to a method to indicate that it is a fact that should
/// be run by the test runner. It can also be extended to support a customized definition
/// of a test method.
/// </para>
/// </summary>
[
AttributeUsage
(
AttributeTargets
.
Method
,
AllowMultiple
=
false
)]
[
XunitTestCaseDiscoverer
(
"StackExchange.Redis.Tests.FactDiscoverer"
,
"StackExchange.Redis.Tests"
)]
...
...
@@ -32,13 +33,14 @@ public override string Skip
}
/// <summary>
///
Override for <see cref="Xunit.TheoryAttribute"/> that truncates our DisplayName down.
///
///
<para>Override for <see cref="Xunit.TheoryAttribute"/> that truncates our DisplayName down.</para>
///
<para>
/// Marks a test method as being a data theory. Data theories are tests which are
/// fed various bits of data from a data source, mapping to parameters on the test
/// method. If the data source contains multiple rows, then the test method is executed
/// multiple times (once with each data row). Data is provided by attributes which
/// derive from Xunit.Sdk.DataAttribute (notably, Xunit.InlineDataAttribute and Xunit.MemberDataAttribute).
/// </para>
/// </summary>
[
AttributeUsage
(
AttributeTargets
.
Method
,
AllowMultiple
=
false
)]
[
XunitTestCaseDiscoverer
(
"StackExchange.Redis.Tests.TheoryDiscoverer"
,
"StackExchange.Redis.Tests"
)]
...
...
StackExchange.Redis/StackExchange/Redis/ConcurrentProfileStorageCollection.cs
View file @
4e5bbd11
...
...
@@ -4,23 +4,23 @@
namespace
StackExchange.Redis
{
/// <summary>
/// A collection of IProfiledCommands.
///
/// This is a very light weight data structure, only supporting enumeration.
///
/// <para>A collection of IProfiledCommands.</para>
/// <para>This is a very light weight data structure, only supporting enumeration.</para>
/// <para>
/// While it implements IEnumerable, it there are fewer allocations if one uses
/// it's explicit GetEnumerator() method. Using `foreach` does this automatically.
///
///
This type is not threadsafe.
///
</para>
///
<para>This type is not threadsafe.</para>
/// </summary>
public
struct
ProfiledCommandEnumerable
:
IEnumerable
<
IProfiledCommand
>
{
/// <summary>
/// <para>
/// Implements IEnumerator for ProfiledCommandEnumerable.
/// This implementation is comparable to List.Enumerator and Dictionary.Enumerator,
/// and is provided to reduce allocations in the common (ie. foreach) case.
///
///
This type is not threadsafe.
///
</para>
///
<para>This type is not threadsafe.</para>
/// </summary>
public
struct
Enumerator
:
IEnumerator
<
IProfiledCommand
>
{
...
...
@@ -89,10 +89,11 @@ internal ProfiledCommandEnumerable(ProfileStorage head)
}
/// <summary>
/// <para>
/// Returns an implementor of IEnumerator that, provided it isn't accessed
/// though an interface, avoids allocations.
///
///
`foreach` will automatically use this method.
///
</para>
///
<para>`foreach` will automatically use this method.</para>
/// </summary>
public
Enumerator
GetEnumerator
()
=>
new
Enumerator
(
Head
);
...
...
@@ -102,10 +103,11 @@ internal ProfiledCommandEnumerable(ProfileStorage head)
}
/// <summary>
/// <para>
/// A thread-safe collection tailored to the "always append, with high contention, then enumerate once with no contention"
/// behavior of our profiling.
///
///
Performs better than ConcurrentBag, which is important since profiling code shouldn't impact timings.
///
</para>
///
<para>Performs better than ConcurrentBag, which is important since profiling code shouldn't impact timings.</para>
/// </summary>
internal
sealed
class
ConcurrentProfileStorageCollection
{
...
...
@@ -137,13 +139,10 @@ internal static int CountInPool()
}
/// <summary>
/// This method is thread-safe.
///
/// Adds an element to the bag.
///
/// Order is not preserved.
///
/// The element can only be a member of *one* bag.
/// <para>This method is thread-safe.</para>
/// <para>Adds an element to the bag.</para>
/// <para>Order is not preserved.</para>
/// <para>The element can only be a member of *one* bag.</para>
/// </summary>
/// <param name="command">The command to add.</param>
public
void
Add
(
ProfileStorage
command
)
...
...
@@ -163,12 +162,12 @@ public void Add(ProfileStorage command)
}
/// <summary>
/// <para>
/// This method returns an enumerable view of the bag, and returns it to
/// an internal pool for reuse by GetOrCreate().
///
/// It is not thread safe.
///
/// It should only be called once the bag is finished being mutated.
/// </para>
/// <para>It is not thread safe.</para>
/// <para>It should only be called once the bag is finished being mutated.</para>
/// </summary>
public
ProfiledCommandEnumerable
EnumerateAndReturnForReuse
()
{
...
...
@@ -194,10 +193,11 @@ public void ReturnForReuse()
}
/// <summary>
///
Returns a ConcurrentProfileStorageCollection to use.
///
///
<para>Returns a ConcurrentProfileStorageCollection to use.</para>
///
<para>
/// It *may* have allocated a new one, or it may return one that has previously been released.
/// To return the collection, call EnumerateAndReturnForReuse()
/// </para>
/// </summary>
public
static
ConcurrentProfileStorageCollection
GetOrCreate
()
{
...
...
StackExchange.Redis/StackExchange/Redis/ConnectionMultiplexer.Profiling.cs
View file @
4e5bbd11
...
...
@@ -10,11 +10,12 @@ public partial class ConnectionMultiplexer
internal
ProfileContextTracker
profiledCommands
;
/// <summary>
///
Sets an IProfiler instance for this ConnectionMultiplexer.
///
///
<para>Sets an IProfiler instance for this ConnectionMultiplexer.</para>
///
<para>
/// An IProfiler instances is used to determine which context to associate an
/// IProfiledCommand with. See BeginProfiling(object) and FinishProfiling(object)
/// for more details.
/// </para>
/// </summary>
/// <param name="profiler">The profiler to register.</param>
public
void
RegisterProfiler
(
IProfiler
profiler
)
...
...
@@ -26,14 +27,13 @@ public void RegisterProfiler(IProfiler profiler)
}
/// <summary>
///
Begins profiling for the given context.
///
///
<para>Begins profiling for the given context.</para>
///
<para>
/// If the same context object is returned by the registered IProfiler, the IProfiledCommands
/// will be associated with each other.
///
/// Call FinishProfiling with the same context to get the assocated commands.
///
/// Note that forContext cannot be a WeakReference or a WeakReference<T>
/// </para>
/// <para>Call FinishProfiling with the same context to get the assocated commands.</para>
/// <para>Note that forContext cannot be a WeakReference or a WeakReference<T</para>>
/// </summary>
/// <param name="forContext">The context to begin profiling.</param>
public
void
BeginProfiling
(
object
forContext
)
...
...
@@ -49,9 +49,8 @@ public void BeginProfiling(object forContext)
}
/// <summary>
/// Stops profiling for the given context, returns all IProfiledCommands associated.
///
/// By default this may do a sweep for dead profiling contexts, you can disable this by passing "allowCleanupSweep: false".
/// <para>Stops profiling for the given context, returns all IProfiledCommands associated.</para>
/// <para>By default this may do a sweep for dead profiling contexts, you can disable this by passing "allowCleanupSweep: false".</para>
/// </summary>
/// <param name="forContext">The context to begin profiling.</param>
/// <param name="allowCleanupSweep">Whether to allow cleanup of old profiling sessions.</param>
...
...
StackExchange.Redis/StackExchange/Redis/Enums/RetransmissionReasonType.cs
View file @
4e5bbd11
namespace
StackExchange.Redis
{
/// <summary>
/// <para>
/// If an IProfiledCommand is a retransmission of a previous command, this enum
/// is used to indicate what prompted the retransmission.
///
/// </para>
/// <para>
/// This can be used to distinguish between transient causes (moving hashslots, joining nodes, etc.)
/// and incorrect routing.
/// </para>
/// </summary>
public
enum
RetransmissionReasonType
{
...
...
StackExchange.Redis/StackExchange/Redis/Interfaces/IProfiler.cs
View file @
4e5bbd11
...
...
@@ -4,10 +4,11 @@
namespace
StackExchange.Redis
{
/// <summary>
///
A profiled command against a redis instance.
///
///
<para>A profiled command against a redis instance.</para>
///
<para>
/// TimeSpans returned by this interface use a high precision timer if possible.
/// DateTimes returned by this interface are no more precise than DateTime.UtcNow.
/// </para>
/// </summary>
public
interface
IProfiledCommand
{
...
...
StackExchange.Redis/StackExchange/Redis/LuaScript.cs
View file @
4e5bbd11
...
...
@@ -6,15 +6,16 @@
namespace
StackExchange.Redis
{
/// <summary>
///
Represents a Lua script that can be executed on Redis.
///
///
<para>Represents a Lua script that can be executed on Redis.</para>
///
<para>
/// Unlike normal Redis Lua scripts, LuaScript can have named parameters (prefixed by a @).
/// Public fields and properties of the passed in object are treated as parameters.
///
/// </para>
/// <para>
/// Parameters of type RedisKey are sent to Redis as KEY (https://redis.io/commands/eval) in addition to arguments,
/// so as to play nicely with Redis Cluster.
///
///
All members of this class are thread safe.
///
</para>
///
<para>All members of this class are thread safe.</para>
/// </summary>
public
sealed
class
LuaScript
{
...
...
@@ -28,9 +29,8 @@ public sealed class LuaScript
public
string
OriginalScript
{
get
;
}
/// <summary>
/// The Lua script that will actually be sent to Redis for execution.
///
/// All @-prefixed parameter names have been replaced at this point.
/// <para>The Lua script that will actually be sent to Redis for execution.</para>
/// <para>All @-prefixed parameter names have been replaced at this point.</para>
/// </summary>
public
string
ExecutableScript
{
get
;
}
...
...
@@ -163,10 +163,11 @@ public Task<RedisResult> EvaluateAsync(IDatabaseAsync db, object ps = null, Redi
}
/// <summary>
/// <para>
/// Loads this LuaScript into the given IServer so it can be run with it's SHA1 hash, instead of
/// passing the full script on each Evaluate or EvaluateAsync call.
///
///
Note: the FireAndForget command flag cannot be set
///
</para>
///
<para>Note: the FireAndForget command flag cannot be set</para>
/// </summary>
/// <param name="server">The server to load the script on.</param>
/// <param name="flags">The command flags to use.</param>
...
...
@@ -182,10 +183,11 @@ public LoadedLuaScript Load(IServer server, CommandFlags flags = CommandFlags.No
}
/// <summary>
/// <para>
/// Loads this LuaScript into the given IServer so it can be run with it's SHA1 hash, instead of
/// passing the full script on each Evaluate or EvaluateAsync call.
///
///
Note: the FireAndForget command flag cannot be set
///
</para>
///
<para>Note: the FireAndForget command flag cannot be set</para>
/// </summary>
/// <param name="server">The server to load the script on.</param>
/// <param name="flags">The command flags to use.</param>
...
...
@@ -202,21 +204,24 @@ public async Task<LoadedLuaScript> LoadAsync(IServer server, CommandFlags flags
}
/// <summary>
///
Represents a Lua script that can be executed on Redis.
///
///
<para>Represents a Lua script that can be executed on Redis.</para>
///
<para>
/// Unlike LuaScript, LoadedLuaScript sends the hash of it's ExecutableScript to Redis rather than pass
/// the whole script on each call. This requires that the script be loaded into Redis before it is used.
///
/// </para>
/// <para>
/// To create a LoadedLuaScript first create a LuaScript via LuaScript.Prepare(string), then
/// call Load(IServer, CommandFlags) on the returned LuaScript.
///
/// </para>
/// <para>
/// Unlike normal Redis Lua scripts, LoadedLuaScript can have named parameters (prefixed by a @).
/// Public fields and properties of the passed in object are treated as parameters.
///
/// </para>
/// <para>
/// Parameters of type RedisKey are sent to Redis as KEY (https://redis.io/commands/eval) in addition to arguments,
/// so as to play nicely with Redis Cluster.
///
///
All members of this class are thread safe.
///
</para>
///
<para>All members of this class are thread safe.</para>
/// </summary>
public
sealed
class
LoadedLuaScript
{
...
...
@@ -231,9 +236,8 @@ public sealed class LoadedLuaScript
public
string
ExecutableScript
=>
Original
.
ExecutableScript
;
/// <summary>
/// The SHA1 hash of ExecutableScript.
///
/// This is sent to Redis instead of ExecutableScript during Evaluate and EvaluateAsync calls.
/// <para>The SHA1 hash of ExecutableScript.</para>
/// <para>This is sent to Redis instead of ExecutableScript during Evaluate and EvaluateAsync calls.</para>
/// </summary>
public
byte
[]
Hash
{
get
;
}
...
...
@@ -247,10 +251,11 @@ internal LoadedLuaScript(LuaScript original, byte[] hash)
}
/// <summary>
///
Evaluates this LoadedLuaScript against the given database, extracting parameters for the passed in object if any.
///
///
<para>Evaluates this LoadedLuaScript against the given database, extracting parameters for the passed in object if any.</para>
///
<para>
/// This method sends the SHA1 hash of the ExecutableScript instead of the script itself. If the script has not
/// been loaded into the passed Redis instance it will fail.
/// </para>
/// </summary>
/// <param name="db">The redis databse to evaluate against.</param>
/// <param name="ps">The parameter object to use.</param>
...
...
@@ -263,10 +268,11 @@ public RedisResult Evaluate(IDatabase db, object ps = null, RedisKey? withKeyPre
}
/// <summary>
///
Evaluates this LoadedLuaScript against the given database, extracting parameters for the passed in object if any.
///
///
<para>Evaluates this LoadedLuaScript against the given database, extracting parameters for the passed in object if any.</para>
///
<para>
/// This method sends the SHA1 hash of the ExecutableScript instead of the script itself. If the script has not
/// been loaded into the passed Redis instance it will fail.
/// </para>
/// </summary>
/// <param name="db">The redis databse to evaluate against.</param>
/// <param name="ps">The parameter object to use.</param>
...
...
StackExchange.Redis/StackExchange/Redis/ProfileContextTracker.cs
View file @
4e5bbd11
...
...
@@ -11,14 +11,13 @@ namespace StackExchange.Redis
internal
sealed
class
ProfileContextTracker
{
/// <summary>
/// Necessary, because WeakReference can't be readily comparable (since the reference is... weak).
///
/// This lets us detect leaks* with some reasonable confidence, and cleanup periodically.
///
/// <para>Necessary, because WeakReference can't be readily comparable (since the reference is... weak).</para>
/// <para>This lets us detect leaks* with some reasonable confidence, and cleanup periodically.</para>
/// <para>
/// Some calisthenics are done to avoid allocating WeakReferences for no reason, as often
/// we're just looking up ProfileStorage.
///
///
* Somebody starts profiling, but for whatever reason never *stops* with a context object
///
</para>
///
<para>* Somebody starts profiling, but for whatever reason never *stops* with a context object</para>
/// </summary>
private
struct
ProfileContextCell
:
IEquatable
<
ProfileContextCell
>
{
...
...
@@ -47,20 +46,22 @@ private ProfileContextCell(object forObj, bool isEphemeral)
}
/// <summary>
///
Suitable for use as a key into something.
///
///
<para>Suitable for use as a key into something.</para>
///
<para>
/// This instance **WILL NOT** keep forObj alive, so it can
/// be copied out of the calling method's scope.
/// </para>
/// </summary>
/// <param name="forObj">The object to get a context for.</param>
public
static
ProfileContextCell
ToStoreUnder
(
object
forObj
)
=>
new
ProfileContextCell
(
forObj
,
isEphemeral
:
false
);
/// <summary>
///
Only suitable for looking up.
///
///
<para>Only suitable for looking up.</para>
///
<para>
/// This instance **ABSOLUTELY WILL** keep forObj alive, so this
/// had better not be copied into anything outside the scope of the
/// calling method.
/// </para>
/// </summary>
/// <param name="forObj">The object to lookup a context by.</param>
public
static
ProfileContextCell
ToLookupBy
(
object
forObj
)
=>
new
ProfileContextCell
(
forObj
,
isEphemeral
:
true
);
...
...
@@ -130,9 +131,8 @@ public ProfileContextTracker()
}
/// <summary>
/// Registers the passed context with a collection that can be retried with subsequent calls to TryGetValue.
///
/// Returns false if the passed context object is already registered.
/// <para>Registers the passed context with a collection that can be retried with subsequent calls to TryGetValue.</para>
/// <para>Returns false if the passed context object is already registered.</para>
/// </summary>
/// <param name="ctx">The context to use.</param>
public
bool
TryCreate
(
object
ctx
)
...
...
@@ -146,10 +146,11 @@ public bool TryCreate(object ctx)
}
/// <summary>
/// <para>
/// Returns true and sets val to the tracking collection associated with the given context if the context
/// was registered with TryCreate.
///
///
Otherwise returns false and sets val to null.
///
</para>
///
<para>Otherwise returns false and sets val to null.</para>
/// </summary>
/// <param name="ctx">The context to get a value for.</param>
/// <param name="val">The collection (if present) for <paramref name="ctx"/>.</param>
...
...
@@ -160,13 +161,15 @@ public bool TryGetValue(object ctx, out ConcurrentProfileStorageCollection val)
}
/// <summary>
/// <para>
/// Removes a context, setting all commands to a (non-thread safe) enumerable of
/// all the commands attached to that context.
///
///
If the context was never registered, will return false and set commands to null.
///
///
</para>
///
<para>If the context was never registered, will return false and set commands to null.</para>
///
<para>
/// Subsequent calls to TryRemove with the same context will return false unless it is
/// re-registered with TryCreate.
/// </para>
/// </summary>
/// <param name="ctx">The context to remove for.</param>
/// <param name="commands">The commands to remove.</param>
...
...
@@ -219,4 +222,4 @@ public bool TryCleanup()
return
true
;
}
}
}
\ No newline at end of file
}
StackExchange.Redis/StackExchange/Redis/RedisKey.cs
View file @
4e5bbd11
...
...
@@ -277,19 +277,21 @@ internal static byte[] ConcatenateBytes(byte[] a, object b, byte[] c)
}
/// <summary>
///
Prepends p to this RedisKey, returning a new RedisKey.
///
///
<para>Prepends p to this RedisKey, returning a new RedisKey.</para>
///
<para>
/// Avoids some allocations if possible, repeated Prepend/Appends make
/// it less possible.
/// </para>
/// </summary>
/// <param name="prefix">The prefix to prepend.</param>
public
RedisKey
Prepend
(
RedisKey
prefix
)
=>
WithPrefix
(
prefix
,
this
);
/// <summary>
///
Appends p to this RedisKey, returning a new RedisKey.
///
///
<para>Appends p to this RedisKey, returning a new RedisKey.</para>
///
<para>
/// Avoids some allocations if possible, repeated Prepend/Appends make
/// it less possible.
/// </para>
/// </summary>
/// <param name="suffix">The suffix to append.</param>
public
RedisKey
Append
(
RedisKey
suffix
)
=>
WithPrefix
(
this
,
suffix
);
...
...
StackExchange.Redis/StackExchange/Redis/RedisValue.cs
View file @
4e5bbd11
...
...
@@ -432,6 +432,7 @@ internal static RedisValue TryParse(object obj)
/// <summary>
/// Creates a new <see cref="RedisValue"/> from a <see cref="T:ReadOnlyMemory{byte}"/>.
/// </summary>
/// <param name="value">The <see cref="T:ReadOnlyMemory{byte}"/> to convert to a <see cref="RedisValue"/>.</param>
public
static
implicit
operator
RedisValue
(
ReadOnlyMemory
<
byte
>
value
)
{
if
(
value
.
Length
==
0
)
return
EmptyString
;
...
...
@@ -440,6 +441,7 @@ internal static RedisValue TryParse(object obj)
/// <summary>
/// Creates a new <see cref="RedisValue"/> from a <see cref="T:Memory{byte}"/>.
/// </summary>
/// <param name="value">The <see cref="T:Memory{byte}"/> to convert to a <see cref="RedisValue"/>.</param>
public
static
implicit
operator
RedisValue
(
Memory
<
byte
>
value
)
=>
(
ReadOnlyMemory
<
byte
>)
value
;
/// <summary>
...
...
StackExchange.Redis/StackExchange/Redis/ScriptParameterMapper.cs
View file @
4e5bbd11
...
...
@@ -272,16 +272,19 @@ private static void PrefixIfNeeded(ILGenerator il, LocalBuilder needsPrefixBool,
}
/// <summary>
///
Creates a Func that extracts parameters from the given type for use by a LuaScript.
///
///
<para>Creates a Func that extracts parameters from the given type for use by a LuaScript.</para>
///
<para>
/// Members that are RedisKey's get extracted to be passed in as keys to redis; all members that
/// appear in the script get extracted as RedisValue arguments to be sent up as args.
///
/// </para>
/// <para>
/// We send all values as arguments so we don't have to prepare the same script for different parameter
/// types.
///
/// </para>
/// <para>
/// The created Func takes a RedisKey, which will be prefixed to all keys (and arguments of type RedisKey) for
/// keyspace isolation.
/// </para>
/// </summary>
/// <param name="t">The type to extract for.</param>
/// <param name="script">The script to extract for.</param>
...
...
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