Commit 89b8f64b authored by Matthew Peterson's avatar Matthew Peterson Committed by Marc Gravell

Made markdown files more readable in Visual Studios by changing code highlighting (#1053)

parent 8761282d
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
The central object in StackExchange.Redis is the `ConnectionMultiplexer` class in the `StackExchange.Redis` namespace; this is the object that hides away the details of multiple servers. Because the `ConnectionMultiplexer` does a lot, it is designed to be **shared and reused** between callers. You should not create a `ConnectionMultiplexer` per operation. It is fully thread-safe and ready for this usage. In all the subsequent examples it will be assumed that you have a `ConnectionMultiplexer` instance stored away for re-use. But for now, let's create one. This is done using `ConnectionMultiplexer.Connect` or `ConnectionMultiplexer.ConnectAsync`, passing in either a configuration string or a `ConfigurationOptions` object. The configuration string can take the form of a comma-delimited series of nodes, so let's just connect to an instance on the local machine on the default port (6379): The central object in StackExchange.Redis is the `ConnectionMultiplexer` class in the `StackExchange.Redis` namespace; this is the object that hides away the details of multiple servers. Because the `ConnectionMultiplexer` does a lot, it is designed to be **shared and reused** between callers. You should not create a `ConnectionMultiplexer` per operation. It is fully thread-safe and ready for this usage. In all the subsequent examples it will be assumed that you have a `ConnectionMultiplexer` instance stored away for re-use. But for now, let's create one. This is done using `ConnectionMultiplexer.Connect` or `ConnectionMultiplexer.ConnectAsync`, passing in either a configuration string or a `ConfigurationOptions` object. The configuration string can take the form of a comma-delimited series of nodes, so let's just connect to an instance on the local machine on the default port (6379):
```C# ```csharp
using StackExchange.Redis; using StackExchange.Redis;
... ...
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost");
...@@ -14,7 +14,7 @@ Note that `ConnectionMultiplexer` implements `IDisposable` and can be disposed w ...@@ -14,7 +14,7 @@ Note that `ConnectionMultiplexer` implements `IDisposable` and can be disposed w
A more complicated scenario might involve a master/slave setup; for this usage, simply specify all the desired nodes that make up that logical redis tier (it will automatically identify the master): A more complicated scenario might involve a master/slave setup; for this usage, simply specify all the desired nodes that make up that logical redis tier (it will automatically identify the master):
```C# ```csharp
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("server1:6379,server2:6379"); ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("server1:6379,server2:6379");
``` ```
...@@ -31,13 +31,13 @@ Using a redis database ...@@ -31,13 +31,13 @@ Using a redis database
Accessing a redis database is as simple as: Accessing a redis database is as simple as:
```C# ```csharp
IDatabase db = redis.GetDatabase(); IDatabase db = redis.GetDatabase();
``` ```
The object returned from `GetDatabase` is a cheap pass-thru object, and does not need to be stored. Note that redis supports multiple databases (although this is not supported on "cluster"); this can be optionally specified in the call to `GetDatabase`. Additionally, if you plan to make use of the asynchronous API and you require the [`Task.AsyncState`][2] to have a value, this can also be specified: The object returned from `GetDatabase` is a cheap pass-thru object, and does not need to be stored. Note that redis supports multiple databases (although this is not supported on "cluster"); this can be optionally specified in the call to `GetDatabase`. Additionally, if you plan to make use of the asynchronous API and you require the [`Task.AsyncState`][2] to have a value, this can also be specified:
```C# ```csharp
int databaseNumber = ... int databaseNumber = ...
object asyncState = ... object asyncState = ...
IDatabase db = redis.GetDatabase(databaseNumber, asyncState); IDatabase db = redis.GetDatabase(databaseNumber, asyncState);
...@@ -47,7 +47,7 @@ Once you have the `IDatabase`, it is simply a case of using the [redis API](http ...@@ -47,7 +47,7 @@ Once you have the `IDatabase`, it is simply a case of using the [redis API](http
The simplest operation would be to store and retrieve a value: The simplest operation would be to store and retrieve a value:
```C# ```csharp
string value = "abcdefg"; string value = "abcdefg";
db.StringSet("mykey", value); db.StringSet("mykey", value);
... ...
...@@ -57,7 +57,7 @@ Console.WriteLine(value); // writes: "abcdefg" ...@@ -57,7 +57,7 @@ Console.WriteLine(value); // writes: "abcdefg"
Note that the `String...` prefix here denotes the [String redis type](http://redis.io/topics/data-types), and is largely separate to the [.NET String type][3], although both can store text data. However, redis allows raw binary data for both keys and values - the usage is identical: Note that the `String...` prefix here denotes the [String redis type](http://redis.io/topics/data-types), and is largely separate to the [.NET String type][3], although both can store text data. However, redis allows raw binary data for both keys and values - the usage is identical:
```C# ```csharp
byte[] key = ..., value = ...; byte[] key = ..., value = ...;
db.StringSet(key, value); db.StringSet(key, value);
... ...
...@@ -71,13 +71,13 @@ Using redis pub/sub ...@@ -71,13 +71,13 @@ Using redis pub/sub
Another common use of redis is as a [pub/sub message](http://redis.io/topics/pubsub) distribution tool; this is also simple, and in the event of connection failure, the `ConnectionMultiplexer` will handle all the details of re-subscribing to the requested channels. Another common use of redis is as a [pub/sub message](http://redis.io/topics/pubsub) distribution tool; this is also simple, and in the event of connection failure, the `ConnectionMultiplexer` will handle all the details of re-subscribing to the requested channels.
```C# ```csharp
ISubscriber sub = redis.GetSubscriber(); ISubscriber sub = redis.GetSubscriber();
``` ```
Again, the object returned from `GetSubscriber` is a cheap pass-thru object that does not need to be stored. The pub/sub API has no concept of databases, but as before we can optionally provide an async-state. Note that all subscriptions are global: they are not scoped to the lifetime of the `ISubscriber` instance. The pub/sub features in redis use named "channels"; channels do not need to be defined in advance on the server (an interesting use here is things like per-user notification channels, which is what drives parts of the realtime updates on [Stack Overflow](http://stackoverflow.com)). As is common in .NET, subscriptions take the form of callback delegates which accept the channel-name and the message: Again, the object returned from `GetSubscriber` is a cheap pass-thru object that does not need to be stored. The pub/sub API has no concept of databases, but as before we can optionally provide an async-state. Note that all subscriptions are global: they are not scoped to the lifetime of the `ISubscriber` instance. The pub/sub features in redis use named "channels"; channels do not need to be defined in advance on the server (an interesting use here is things like per-user notification channels, which is what drives parts of the realtime updates on [Stack Overflow](http://stackoverflow.com)). As is common in .NET, subscriptions take the form of callback delegates which accept the channel-name and the message:
```C# ```csharp
sub.Subscribe("messages", (channel, message) => { sub.Subscribe("messages", (channel, message) => {
Console.WriteLine((string)message); Console.WriteLine((string)message);
}); });
...@@ -85,7 +85,7 @@ sub.Subscribe("messages", (channel, message) => { ...@@ -85,7 +85,7 @@ sub.Subscribe("messages", (channel, message) => {
Separately (and often in a separate process on a separate machine) you can publish to this channel: Separately (and often in a separate process on a separate machine) you can publish to this channel:
```C# ```csharp
sub.Publish("messages", "hello"); sub.Publish("messages", "hello");
``` ```
...@@ -98,19 +98,19 @@ Accessing individual servers ...@@ -98,19 +98,19 @@ Accessing individual servers
For maintenance purposes, it is sometimes necessary to issue server-specific commands: For maintenance purposes, it is sometimes necessary to issue server-specific commands:
```C# ```csharp
IServer server = redis.GetServer("localhost", 6379); IServer server = redis.GetServer("localhost", 6379);
``` ```
The `GetServer` method will accept an [`EndPoint`](http://msdn.microsoft.com/en-us/library/system.net.endpoint(v=vs.110).aspx) or the name/value pair that uniquely identify the server. As before, the object returned from `GetServer` is a cheap pass-thru object that does not need to be stored, and async-state can be optionally specified. Note that the set of available endpoints is also available: The `GetServer` method will accept an [`EndPoint`](http://msdn.microsoft.com/en-us/library/system.net.endpoint(v=vs.110).aspx) or the name/value pair that uniquely identify the server. As before, the object returned from `GetServer` is a cheap pass-thru object that does not need to be stored, and async-state can be optionally specified. Note that the set of available endpoints is also available:
```C# ```csharp
EndPoint[] endpoints = redis.GetEndPoints(); EndPoint[] endpoints = redis.GetEndPoints();
``` ```
From the `IServer` instance, the [Server commands](http://redis.io/commands#server) are available; for example: From the `IServer` instance, the [Server commands](http://redis.io/commands#server) are available; for example:
```C# ```csharp
DateTime lastSave = server.LastSave(); DateTime lastSave = server.LastSave();
ClientInfo[] clients = server.ClientList(); ClientInfo[] clients = server.ClientList();
``` ```
...@@ -131,7 +131,7 @@ The synchronous usage is already shown in the examples above. This is the simple ...@@ -131,7 +131,7 @@ The synchronous usage is already shown in the examples above. This is the simple
For asynchronous usage, the key difference is the `Async` suffix on methods, and (typically) the use of the `await` language feature. For example: For asynchronous usage, the key difference is the `Async` suffix on methods, and (typically) the use of the `await` language feature. For example:
```C# ```csharp
string value = "abcdefg"; string value = "abcdefg";
await db.StringSetAsync("mykey", value); await db.StringSetAsync("mykey", value);
... ...
...@@ -141,7 +141,7 @@ Console.WriteLine(value); // writes: "abcdefg" ...@@ -141,7 +141,7 @@ Console.WriteLine(value); // writes: "abcdefg"
The fire-and-forget usage is accessed by the optional `CommandFlags flags` parameter on all methods (defaults to none). In this usage, the method returns the default value immediately (so a method that normally returns a `String` will always return `null`, and a method that normally returns an `Int64` will always return `0`). The operation will continue in the background. A typical use-case of this might be to increment page-view counts: The fire-and-forget usage is accessed by the optional `CommandFlags flags` parameter on all methods (defaults to none). In this usage, the method returns the default value immediately (so a method that normally returns a `String` will always return `null`, and a method that normally returns an `Int64` will always return `0`). The operation will continue in the background. A typical use-case of this might be to increment page-view counts:
```C# ```csharp
db.StringIncrement(pageKey, flags: CommandFlags.FireAndForget); db.StringIncrement(pageKey, flags: CommandFlags.FireAndForget);
``` ```
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
Because there are lots of different ways to configure redis, StackExchange.Redis offers a rich configuration model, which is invoked when calling `Connect` (or `ConnectAsync`): Because there are lots of different ways to configure redis, StackExchange.Redis offers a rich configuration model, which is invoked when calling `Connect` (or `ConnectAsync`):
```C# ```csharp
var conn = ConnectionMultiplexer.Connect(configuration); var conn = ConnectionMultiplexer.Connect(configuration);
``` ```
...@@ -19,31 +19,31 @@ Basic Configuration Strings ...@@ -19,31 +19,31 @@ Basic Configuration Strings
The *simplest* configuration example is just the host name: The *simplest* configuration example is just the host name:
```C# ```csharp
var conn = ConnectionMultiplexer.Connect("localhost"); var conn = ConnectionMultiplexer.Connect("localhost");
``` ```
This will connect to a single server on the local machine using the default redis port (6379). Additional options are simply appended (comma-delimited). Ports are represented with a colon (`:`) as is usual. Configuration *options* include an `=` after the name. For example: This will connect to a single server on the local machine using the default redis port (6379). Additional options are simply appended (comma-delimited). Ports are represented with a colon (`:`) as is usual. Configuration *options* include an `=` after the name. For example:
```C# ```csharp
var conn = ConnectionMultiplexer.Connect("redis0:6380,redis1:6380,allowAdmin=true"); var conn = ConnectionMultiplexer.Connect("redis0:6380,redis1:6380,allowAdmin=true");
``` ```
An overview of mapping between the `string` and `ConfigurationOptions` representation is shown below, but you can switch between them trivially: An overview of mapping between the `string` and `ConfigurationOptions` representation is shown below, but you can switch between them trivially:
```C# ```csharp
ConfigurationOptions options = ConfigurationOptions.Parse(configString); ConfigurationOptions options = ConfigurationOptions.Parse(configString);
``` ```
or: or:
```C# ```csharp
string configString = options.ToString(); string configString = options.ToString();
``` ```
A common usage is to store the *basic* details in a string, and then apply specific details at runtime: A common usage is to store the *basic* details in a string, and then apply specific details at runtime:
```C# ```csharp
string configString = GetRedisConfiguration(); string configString = GetRedisConfiguration();
var options = ConfigurationOptions.Parse(configString); var options = ConfigurationOptions.Parse(configString);
options.ClientName = GetAppName(); // only known at runtime options.ClientName = GetAppName(); // only known at runtime
...@@ -53,7 +53,7 @@ conn = ConnectionMultiplexer.Connect(options); ...@@ -53,7 +53,7 @@ conn = ConnectionMultiplexer.Connect(options);
Microsoft Azure Redis example with password Microsoft Azure Redis example with password
```C# ```csharp
var conn = ConnectionMultiplexer.Connect("contoso5.redis.cache.windows.net,ssl=true,password=..."); var conn = ConnectionMultiplexer.Connect("contoso5.redis.cache.windows.net,ssl=true,password=...");
``` ```
...@@ -98,7 +98,7 @@ Automatic and Manual Configuration ...@@ -98,7 +98,7 @@ Automatic and Manual Configuration
In many common scenarios, StackExchange.Redis will automatically configure a lot of settings, including the server type and version, connection timeouts, and master/slave relationships. Sometimes, though, the commands for this have been disabled on the redis server. In this case, it is useful to provide more information: In many common scenarios, StackExchange.Redis will automatically configure a lot of settings, including the server type and version, connection timeouts, and master/slave relationships. Sometimes, though, the commands for this have been disabled on the redis server. In this case, it is useful to provide more information:
```C# ```csharp
ConfigurationOptions config = new ConfigurationOptions ConfigurationOptions config = new ConfigurationOptions
{ {
EndPoints = EndPoints =
...@@ -127,7 +127,7 @@ Renaming Commands ...@@ -127,7 +127,7 @@ Renaming Commands
A slightly unusual feature of redis is that you can disable and/or rename individual commands. As per the previous example, this is done via the `CommandMap`, but instead of passing a `HashSet<string>` to `Create()` (to indicate the available or unavailable commands), you pass a `Dictionary<string,string>`. All commands not mentioned in the dictionary are assumed to be enabled and not renamed. A `null` or blank value records that the command is disabled. For example: A slightly unusual feature of redis is that you can disable and/or rename individual commands. As per the previous example, this is done via the `CommandMap`, but instead of passing a `HashSet<string>` to `Create()` (to indicate the available or unavailable commands), you pass a `Dictionary<string,string>`. All commands not mentioned in the dictionary are assumed to be enabled and not renamed. A `null` or blank value records that the command is disabled. For example:
```C# ```csharp
var commands = new Dictionary<string,string> { var commands = new Dictionary<string,string> {
{ "info", null }, // disabled { "info", null }, // disabled
{ "select", "use" }, // renamed to SQL equivalent for some reason { "select", "use" }, // renamed to SQL equivalent for some reason
...@@ -150,7 +150,7 @@ Twemproxy ...@@ -150,7 +150,7 @@ Twemproxy
[Twemproxy](https://github.com/twitter/twemproxy) is a tool that allows multiple redis instances to be used as though it were a single server, with inbuilt sharding and fault tolerance (much like redis cluster, but implemented separately). The feature-set available to Twemproxy is reduced. To avoid having to configure this manually, the `Proxy` option can be used: [Twemproxy](https://github.com/twitter/twemproxy) is a tool that allows multiple redis instances to be used as though it were a single server, with inbuilt sharding and fault tolerance (much like redis cluster, but implemented separately). The feature-set available to Twemproxy is reduced. To avoid having to configure this manually, the `Proxy` option can be used:
```C# ```csharp
var options = new ConfigurationOptions var options = new ConfigurationOptions
{ {
EndPoints = { "my-server" }, EndPoints = { "my-server" },
...@@ -176,7 +176,7 @@ ReconnectRetryPolicy can be linear (default), exponential or a custom retry poli ...@@ -176,7 +176,7 @@ ReconnectRetryPolicy can be linear (default), exponential or a custom retry poli
Examples: Examples:
```C# ```csharp
config.ReconnectRetryPolicy = new ExponentialRetry(5000); // defaults maxDeltaBackoff to 10000 ms config.ReconnectRetryPolicy = new ExponentialRetry(5000); // defaults maxDeltaBackoff to 10000 ms
//retry# retry to re-connect after time in milliseconds //retry# retry to re-connect after time in milliseconds
//1 a random value between 5000 and 5500 //1 a random value between 5000 and 5500
......
...@@ -38,7 +38,7 @@ So how do I use them? ...@@ -38,7 +38,7 @@ So how do I use them?
Simple: start from a server, not a database. Simple: start from a server, not a database.
```C# ```csharp
// get the target server // get the target server
var server = conn.GetServer(someServer); var server = conn.GetServer(someServer);
......
...@@ -18,21 +18,21 @@ Keys ...@@ -18,21 +18,21 @@ Keys
StackExchange.Redis represents keys by the `RedisKey` type. The good news, though, is that this has implicit conversions to and from both `string` and `byte[]`, allowing both text and binary keys to be used without any complication. For example, the `StringIncrement` method takes a `RedisKey` as the first parameter, but *you don't need to know that*; for example: StackExchange.Redis represents keys by the `RedisKey` type. The good news, though, is that this has implicit conversions to and from both `string` and `byte[]`, allowing both text and binary keys to be used without any complication. For example, the `StringIncrement` method takes a `RedisKey` as the first parameter, but *you don't need to know that*; for example:
```C# ```csharp
string key = ... string key = ...
db.StringIncrement(key); db.StringIncrement(key);
``` ```
or or
```C# ```csharp
byte[] key = ... byte[] key = ...
db.StringIncrement(key); db.StringIncrement(key);
``` ```
Likewise, there are operations that *return* keys as `RedisKey` - and again, it simply works: Likewise, there are operations that *return* keys as `RedisKey` - and again, it simply works:
```C# ```csharp
string someKey = db.KeyRandom(); string someKey = db.KeyRandom();
``` ```
...@@ -41,13 +41,13 @@ Values ...@@ -41,13 +41,13 @@ Values
StackExchange.Redis represents values by the `RedisValue` type. As with `RedisKey`, there are implicit conversions in place which mean that most of the time you never see this type, for example: StackExchange.Redis represents values by the `RedisValue` type. As with `RedisKey`, there are implicit conversions in place which mean that most of the time you never see this type, for example:
```C# ```csharp
db.StringSet("mykey", "myvalue"); db.StringSet("mykey", "myvalue");
``` ```
However, in addition to text and binary contents, values can also need to represent typed primitive data - most commonly (in .NET terms) `Int32`, `Int64`, `Double` or `Boolean`. Because of this, `RedisValue` provides a lot more conversion support than `RedisKey`: However, in addition to text and binary contents, values can also need to represent typed primitive data - most commonly (in .NET terms) `Int32`, `Int64`, `Double` or `Boolean`. Because of this, `RedisValue` provides a lot more conversion support than `RedisKey`:
```C# ```csharp
db.StringSet("mykey", 123); // this is still a RedisKey and RedisValue db.StringSet("mykey", 123); // this is still a RedisKey and RedisValue
... ...
int i = (int)db.StringGet("mykey"); int i = (int)db.StringGet("mykey");
...@@ -57,14 +57,14 @@ Note that while the conversions from primitives to `RedisValue` are implicit, ma ...@@ -57,14 +57,14 @@ Note that while the conversions from primitives to `RedisValue` are implicit, ma
Note additionally that *when treated numerically*, redis treats a non-existent key as zero; for consistency with this, nil responses are treated as zero: Note additionally that *when treated numerically*, redis treats a non-existent key as zero; for consistency with this, nil responses are treated as zero:
```C# ```csharp
db.KeyDelete("abc"); db.KeyDelete("abc");
int i = (int)db.StringGet("abc"); // this is ZERO int i = (int)db.StringGet("abc"); // this is ZERO
``` ```
If you need to detect the nil condition, then you can check for that: If you need to detect the nil condition, then you can check for that:
```C# ```csharp
db.KeyDelete("abc"); db.KeyDelete("abc");
var value = db.StringGet("abc"); var value = db.StringGet("abc");
bool isNil = value.IsNull; // this is true bool isNil = value.IsNull; // this is true
...@@ -72,7 +72,7 @@ bool isNil = value.IsNull; // this is true ...@@ -72,7 +72,7 @@ bool isNil = value.IsNull; // this is true
or perhaps more simply, just use the provided `Nullable<T>` support: or perhaps more simply, just use the provided `Nullable<T>` support:
```C# ```csharp
db.KeyDelete("abc"); db.KeyDelete("abc");
var value = (int?)db.StringGet("abc"); // behaves as you would expect var value = (int?)db.StringGet("abc"); // behaves as you would expect
``` ```
...@@ -97,7 +97,7 @@ Scripting ...@@ -97,7 +97,7 @@ Scripting
Because of this, the `ScriptEvaluate` method accepts two separate input arrays: one `RedisKey[]` for the keys, one `RedisValue[]` for the values (both are optional, and are assumed to be empty if omitted). This is probably one of the few times that you'll actually need to type `RedisKey` or `RedisValue` in your code, and that is just because of array variance rules: Because of this, the `ScriptEvaluate` method accepts two separate input arrays: one `RedisKey[]` for the keys, one `RedisValue[]` for the values (both are optional, and are assumed to be empty if omitted). This is probably one of the few times that you'll actually need to type `RedisKey` or `RedisValue` in your code, and that is just because of array variance rules:
```C# ```csharp
var result = db.ScriptEvaluate(TransferScript, var result = db.ScriptEvaluate(TransferScript,
new RedisKey[] { from, to }, new RedisValue[] { quantity }); new RedisKey[] { from, to }, new RedisValue[] { quantity });
``` ```
...@@ -106,7 +106,7 @@ var result = db.ScriptEvaluate(TransferScript, ...@@ -106,7 +106,7 @@ var result = db.ScriptEvaluate(TransferScript,
The response uses the `RedisResult` type (this is unique to scripting; usually the API tries to represent the response as directly and clearly as possible). As before, `RedisResult` offers a range of conversion operations - more, in fact than `RedisValue`, because in addition to being interpreted as text, binary, primitives and nullable-primitives, the response can *also* be interpreted as *arrays* of such, for example: The response uses the `RedisResult` type (this is unique to scripting; usually the API tries to represent the response as directly and clearly as possible). As before, `RedisResult` offers a range of conversion operations - more, in fact than `RedisValue`, because in addition to being interpreted as text, binary, primitives and nullable-primitives, the response can *also* be interpreted as *arrays* of such, for example:
```C# ```csharp
string[] items = db.ScriptEvaluate(...); string[] items = db.ScriptEvaluate(...);
``` ```
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
Latency sucks. Modern computers can churn data at an alarming rate, and high speed networking (often with multiple parallel links between important servers) provides enormous bandwidth, but... that damned latency means that computers spend an awful lot of time *waiting for data* and that is one of the several reasons that continuation-based programming is becoming increasingly popular. Let's consider some regular procedural code: Latency sucks. Modern computers can churn data at an alarming rate, and high speed networking (often with multiple parallel links between important servers) provides enormous bandwidth, but... that damned latency means that computers spend an awful lot of time *waiting for data* and that is one of the several reasons that continuation-based programming is becoming increasingly popular. Let's consider some regular procedural code:
```C# ```csharp
string a = db.StringGet("a"); string a = db.StringGet("a");
string b = db.StringGet("b"); string b = db.StringGet("b");
``` ```
...@@ -42,7 +42,7 @@ Because of this, many redis clients allow you to make use of *pipelining*; this ...@@ -42,7 +42,7 @@ Because of this, many redis clients allow you to make use of *pipelining*; this
For example, to pipeline the two gets using procedural (blocking) code, we could use: For example, to pipeline the two gets using procedural (blocking) code, we could use:
```C# ```csharp
var aPending = db.StringGetAsync("a"); var aPending = db.StringGetAsync("a");
var bPending = db.StringGetAsync("b"); var bPending = db.StringGetAsync("b");
var a = db.Wait(aPending); var a = db.Wait(aPending);
...@@ -56,7 +56,7 @@ Fire and Forget ...@@ -56,7 +56,7 @@ Fire and Forget
A special-case of pipelining is when we expressly don't care about the response from a particular operation, which allows our code to continue immediately while the enqueued operation proceeds in the background. Often, this means that we can put concurrent work on the connection from a single caller. This is achieved using the `flags` parameter: A special-case of pipelining is when we expressly don't care about the response from a particular operation, which allows our code to continue immediately while the enqueued operation proceeds in the background. Often, this means that we can put concurrent work on the connection from a single caller. This is achieved using the `flags` parameter:
```C# ```csharp
// sliding expiration // sliding expiration
db.KeyExpire(key, TimeSpan.FromMinutes(5), flags: CommandFlags.FireAndForget); db.KeyExpire(key, TimeSpan.FromMinutes(5), flags: CommandFlags.FireAndForget);
var value = (string)db.StringGet(key); var value = (string)db.StringGet(key);
...@@ -71,7 +71,7 @@ Pipelining is all well and good, but often any single block of code only wants a ...@@ -71,7 +71,7 @@ Pipelining is all well and good, but often any single block of code only wants a
For this reason, the only redis features that StackExchange.Redis does not offer (and *will not ever offer*) are the "blocking pops" ([BLPOP](http://redis.io/commands/blpop), [BRPOP](http://redis.io/commands/brpop) and [BRPOPLPUSH](http://redis.io/commands/brpoplpush)) - because this would allow a single caller to stall the entire multiplexer, blocking all other callers. The only other time that StackExchange.Redis needs to hold work is when verifying pre-conditions for a transaction, which is why StackExchange.Redis encapsulates such conditions into internally managed `Condition` instances. [Read more about transactions here](Transactions). If you feel you want "blocking pops", then I strongly suggest you consider pub/sub instead: For this reason, the only redis features that StackExchange.Redis does not offer (and *will not ever offer*) are the "blocking pops" ([BLPOP](http://redis.io/commands/blpop), [BRPOP](http://redis.io/commands/brpop) and [BRPOPLPUSH](http://redis.io/commands/brpoplpush)) - because this would allow a single caller to stall the entire multiplexer, blocking all other callers. The only other time that StackExchange.Redis needs to hold work is when verifying pre-conditions for a transaction, which is why StackExchange.Redis encapsulates such conditions into internally managed `Condition` instances. [Read more about transactions here](Transactions). If you feel you want "blocking pops", then I strongly suggest you consider pub/sub instead:
```C# ```csharp
sub.Subscribe(channel, delegate { sub.Subscribe(channel, delegate {
string work = db.ListRightPop(key); string work = db.ListRightPop(key);
if (work != null) Process(work); if (work != null) Process(work);
...@@ -96,7 +96,7 @@ Concurrency ...@@ -96,7 +96,7 @@ Concurrency
It should be noted that the pipeline / multiplexer / future-value approach also plays very nicely with continuation-based asynchronous code; for example you could write: It should be noted that the pipeline / multiplexer / future-value approach also plays very nicely with continuation-based asynchronous code; for example you could write:
```C# ```csharp
string value = await db.StringGetAsync(key); string value = await db.StringGetAsync(key);
if (value == null) { if (value == null) {
value = await ComputeValueFromDatabase(...); value = await ComputeValueFromDatabase(...);
......
...@@ -45,7 +45,7 @@ command is sent (via the `IProfiler` interface's `GetContext()` method). ...@@ -45,7 +45,7 @@ command is sent (via the `IProfiler` interface's `GetContext()` method).
A toy example of associating commands issued from many different threads together A toy example of associating commands issued from many different threads together
```C# ```csharp
class ToyProfiler : IProfiler class ToyProfiler : IProfiler
{ {
public ConcurrentDictionary<Thread, object> Contexts = new ConcurrentDictionary<Thread, object>(); public ConcurrentDictionary<Thread, object> Contexts = new ConcurrentDictionary<Thread, object>();
...@@ -106,7 +106,7 @@ At the end, `timings` will contain 16,000 `IProfiledCommand` objects - one for e ...@@ -106,7 +106,7 @@ At the end, `timings` will contain 16,000 `IProfiledCommand` objects - one for e
If instead you did the following: If instead you did the following:
```C# ```csharp
ConnectionMultiplexer conn = /* initialization */; ConnectionMultiplexer conn = /* initialization */;
var profiler = new ToyProfiler(); var profiler = new ToyProfiler();
...@@ -155,7 +155,7 @@ Moving away from toy examples, here's how you can profile StackExchange.Redis in ...@@ -155,7 +155,7 @@ Moving away from toy examples, here's how you can profile StackExchange.Redis in
First register the following `IProfiler` against your `ConnectionMultiplexer`: First register the following `IProfiler` against your `ConnectionMultiplexer`:
```C# ```csharp
public class RedisProfiler : IProfiler public class RedisProfiler : IProfiler
{ {
const string RequestContextKey = "RequestProfilingContext"; const string RequestContextKey = "RequestProfilingContext";
...@@ -183,7 +183,7 @@ public class RedisProfiler : IProfiler ...@@ -183,7 +183,7 @@ public class RedisProfiler : IProfiler
Then, add the following to your Global.asax.cs file: Then, add the following to your Global.asax.cs file:
```C# ```csharp
protected void Application_BeginRequest() protected void Application_BeginRequest()
{ {
var ctxObj = RedisProfiler.CreateContextForCurrentRequest(); var ctxObj = RedisProfiler.CreateContextForCurrentRequest();
......
...@@ -45,7 +45,7 @@ This is achieved by providing the desired `ProfilingSession` object via the call ...@@ -45,7 +45,7 @@ This is achieved by providing the desired `ProfilingSession` object via the call
Probably the most useful general-purpose session-provider is one that provides session automatically and works between `async` calls; this is simply: Probably the most useful general-purpose session-provider is one that provides session automatically and works between `async` calls; this is simply:
```c# ```csharp
class AsyncLocalProfiler class AsyncLocalProfiler
{ {
private readonly AsyncLocal<ProfilingSession> perThreadSession = new AsyncLocal<ProfilingSession>(); private readonly AsyncLocal<ProfilingSession> perThreadSession = new AsyncLocal<ProfilingSession>();
...@@ -76,7 +76,7 @@ A toy example of associating commands issued from many different threads togethe ...@@ -76,7 +76,7 @@ A toy example of associating commands issued from many different threads togethe
1.* 1.*
```C# ```csharp
class ToyProfiler class ToyProfiler
{ {
// note this won't work over "await" boundaries; "AsyncLocal" would be necessary there // note this won't work over "await" boundaries; "AsyncLocal" would be necessary there
...@@ -134,7 +134,7 @@ At the end, `timings` will contain 16,000 `IProfiledCommand` objects - one for e ...@@ -134,7 +134,7 @@ At the end, `timings` will contain 16,000 `IProfiledCommand` objects - one for e
If instead you did the following: If instead you did the following:
```C# ```csharp
ConnectionMultiplexer conn = /* initialization */; ConnectionMultiplexer conn = /* initialization */;
var profiler = new ToyProfiler(); var profiler = new ToyProfiler();
...@@ -179,7 +179,7 @@ Moving away from toy examples, here's how you can profile StackExchange.Redis in ...@@ -179,7 +179,7 @@ Moving away from toy examples, here's how you can profile StackExchange.Redis in
First register the following `IProfiler` against your `ConnectionMultiplexer`: First register the following `IProfiler` against your `ConnectionMultiplexer`:
```C# ```csharp
public class RedisProfiler public class RedisProfiler
{ {
const string RequestContextKey = "RequestProfilingContext"; const string RequestContextKey = "RequestProfilingContext";
...@@ -205,7 +205,7 @@ public class RedisProfiler ...@@ -205,7 +205,7 @@ public class RedisProfiler
Then, add the following to your Global.asax.cs file (where `_redisProfiler` is the *instance* of the profiler): Then, add the following to your Global.asax.cs file (where `_redisProfiler` is the *instance* of the profiler):
```C# ```csharp
protected void Application_BeginRequest() protected void Application_BeginRequest()
{ {
_redisProfiler.CreateSessionForCurrentRequest(); _redisProfiler.CreateSessionForCurrentRequest();
...@@ -225,7 +225,7 @@ protected void Application_EndRequest() ...@@ -225,7 +225,7 @@ protected void Application_EndRequest()
and ensure that the connection has the profiler registered when the connection is created: and ensure that the connection has the profiler registered when the connection is created:
```C# ```csharp
connection.RegisterProfiler(() => _redisProfiler.GetSession()); connection.RegisterProfiler(() => _redisProfiler.GetSession());
``` ```
......
...@@ -12,7 +12,7 @@ This works *particularly* well if messages are generally unrelated. ...@@ -12,7 +12,7 @@ This works *particularly* well if messages are generally unrelated.
For safety, **the default is sequential**; however, it is strongly recommended that you use concurrent processing whenever possible. This is a simple change: For safety, **the default is sequential**; however, it is strongly recommended that you use concurrent processing whenever possible. This is a simple change:
```C# ```csharp
multiplexer.PreserveAsyncOrder = false; multiplexer.PreserveAsyncOrder = false;
``` ```
......
...@@ -11,7 +11,7 @@ all applied in a single unit (i.e. without other connections getting time betwee ...@@ -11,7 +11,7 @@ all applied in a single unit (i.e. without other connections getting time betwee
a `EXEC`, everything is thrown away. Because the commands inside the transaction are queued, you can't make decisions *inside* a `EXEC`, everything is thrown away. Because the commands inside the transaction are queued, you can't make decisions *inside*
the transaction. For example, in a SQL database you might do the following (pseudo-code - illustrative only): the transaction. For example, in a SQL database you might do the following (pseudo-code - illustrative only):
```C# ```csharp
// assign a new unique id only if they don't already // assign a new unique id only if they don't already
// have one, in a transaction to ensure no thread-races // have one, in a transaction to ensure no thread-races
var newId = CreateNewUniqueID(); // optimistic var newId = CreateNewUniqueID(); // optimistic
...@@ -66,7 +66,7 @@ basically pre-canned tests involving `WATCH`, some kind of test, and a check on ...@@ -66,7 +66,7 @@ basically pre-canned tests involving `WATCH`, some kind of test, and a check on
pass, the `MULTI`/`EXEC` is issued; otherwise `UNWATCH` is issued. This is all done in a way that prevents the commands being pass, the `MULTI`/`EXEC` is issued; otherwise `UNWATCH` is issued. This is all done in a way that prevents the commands being
mixed together with other callers. So our example becomes: mixed together with other callers. So our example becomes:
```C# ```csharp
var newId = CreateNewId(); var newId = CreateNewId();
var tran = db.CreateTransaction(); var tran = db.CreateTransaction();
tran.AddCondition(Condition.HashNotExists(custKey, "UniqueID")); tran.AddCondition(Condition.HashNotExists(custKey, "UniqueID"));
...@@ -88,7 +88,7 @@ Inbuilt operations via `When` ...@@ -88,7 +88,7 @@ Inbuilt operations via `When`
It should also be noted that many common scenarios (in particular: key/hash existence, like in the above) have been anticipated by Redis, and single-operation It should also be noted that many common scenarios (in particular: key/hash existence, like in the above) have been anticipated by Redis, and single-operation
atomic commands exist. These are accessed via the `When` parameter - so our previous example can *also* be written as: atomic commands exist. These are accessed via the `When` parameter - so our previous example can *also* be written as:
```C# ```csharp
var newId = CreateNewId(); var newId = CreateNewId();
bool wasSet = db.HashSet(custKey, "UniqueID", newId, When.NotExists); bool wasSet = db.HashSet(custKey, "UniqueID", newId, When.NotExists);
``` ```
...@@ -110,7 +110,7 @@ EVAL "if redis.call('hexists', KEYS[1], 'UniqueId') then return redis.call('hset ...@@ -110,7 +110,7 @@ EVAL "if redis.call('hexists', KEYS[1], 'UniqueId') then return redis.call('hset
This can be used in StackExchange.Redis via: This can be used in StackExchange.Redis via:
```C# ```csharp
var wasSet = (bool) db.ScriptEvaluate(@"if redis.call('hexists', KEYS[1], 'UniqueId') then return redis.call('hset', KEYS[1], 'UniqueId', ARGV[1]) else return 0 end", var wasSet = (bool) db.ScriptEvaluate(@"if redis.call('hexists', KEYS[1], 'UniqueId') then return redis.call('hset', KEYS[1], 'UniqueId', ARGV[1]) else return 0 end",
new RedisKey[] { custKey }, new RedisValue[] { newId }); new RedisKey[] { custKey }, new RedisValue[] { newId });
``` ```
......
...@@ -21,7 +21,7 @@ What it is: ...@@ -21,7 +21,7 @@ What it is:
Example usage: Example usage:
```c# ```csharp
using System; using System;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment