Skip to content

Commit

Permalink
Fix redis incr ops
Browse files Browse the repository at this point in the history
  • Loading branch information
ejsmith committed Jan 13, 2018
1 parent 9610192 commit 66a1daa
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 14 deletions.
22 changes: 22 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,25 @@ UpgradeLog*.htm
# Microsoft Fakes
FakesAssemblies/
.vs/config/applicationhost.config

# Common IntelliJ Platform excludes

# User specific
**/.idea/**/workspace.xml
**/.idea/**/tasks.xml
**/.idea/shelf/*
**/.idea/dictionaries

# Sensitive or high-churn files
**/.idea/**/dataSources/
**/.idea/**/dataSources.ids
**/.idea/**/dataSources.xml
**/.idea/**/dataSources.local.xml
**/.idea/**/sqlDataSources.xml
**/.idea/**/dynamic.xml

# Rider
# Rider auto-generates .iml files, and contentModel.xml
**/.idea/**/*.iml
**/.idea/**/contentModel.xml
**/.idea/**/modules.xml
28 changes: 28 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/test/Foundatio.Benchmarks/bin/Debug/netcoreapp2.0/Foundatio.Benchmarks.dll",
"args": [],
"cwd": "${workspaceFolder}/test/Foundatio.Benchmarks",
// For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window
"console": "internalConsole",
"stopAtEntry": false,
"internalConsoleOptions": "openOnSessionStart"
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}
33 changes: 33 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"group": {
"kind": "build",
"isDefault": true
},
"args": [
"build",
"${workspaceFolder}/test/Foundatio.Redis.Tests/Foundatio.Redis.Tests.csproj"
],
"problemMatcher": "$msCompile"
},
{
"label": "test",
"command": "dotnet",
"type": "process",
"group": {
"kind": "test",
"isDefault": true
},
"args": [
"test",
"${workspaceFolder}/test/Foundatio.Redis.Tests/Foundatio.Redis.Tests.csproj"
],
"problemMatcher": "$msCompile"
}
]
}
82 changes: 70 additions & 12 deletions src/Foundatio.Redis/Cache/RedisCacheClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,28 @@ public async Task<double> SetIfHigherAsync(string key, double value, TimeSpan? e

await LoadScriptsAsync().AnyContext();

var result = await Database.ScriptEvaluateAsync(_setIfHigherScript, new { key, value, expires = expiresIn?.TotalSeconds }).AnyContext();
return (double)result;
if (expiresIn.HasValue) {
var result = await Database.ScriptEvaluateAsync(_setIfHigherScript, new { key, value, expires = expiresIn.Value.TotalSeconds }).AnyContext();
return (double)result;
} else {
var result = await Database.ScriptEvaluateAsync(_setIfHigherScript, new { key, value, expires = RedisValue.EmptyString }).AnyContext();
return (double)result;
}
}

public async Task<long> SetIfHigherAsync(string key, long value, TimeSpan? expiresIn = null) {
if (String.IsNullOrEmpty(key))
throw new ArgumentNullException(nameof(key), "Key cannot be null or empty.");

await LoadScriptsAsync().AnyContext();

if (expiresIn.HasValue) {
var result = await Database.ScriptEvaluateAsync(_setIfHigherScript, new { key, value, expires = expiresIn.Value.TotalSeconds }).AnyContext();
return (long)result;
} else {
var result = await Database.ScriptEvaluateAsync(_setIfHigherScript, new { key, value, expires = RedisValue.EmptyString }).AnyContext();
return (long)result;
}
}

public async Task<double> SetIfLowerAsync(string key, double value, TimeSpan? expiresIn = null) {
Expand All @@ -219,8 +239,28 @@ public async Task<double> SetIfLowerAsync(string key, double value, TimeSpan? ex

await LoadScriptsAsync().AnyContext();

var result = await Database.ScriptEvaluateAsync(_setIfLowerScript, new { key, value, expires = expiresIn?.TotalSeconds }).AnyContext();
return (double)result;
if (expiresIn.HasValue) {
var result = await Database.ScriptEvaluateAsync(_setIfLowerScript, new { key, value, expires = expiresIn.Value.TotalSeconds }).AnyContext();
return (double)result;
} else {
var result = await Database.ScriptEvaluateAsync(_setIfLowerScript, new { key, value, expires = RedisValue.EmptyString }).AnyContext();
return (double)result;
}
}

public async Task<long> SetIfLowerAsync(string key, long value, TimeSpan? expiresIn = null) {
if (String.IsNullOrEmpty(key))
throw new ArgumentNullException(nameof(key), "Key cannot be null or empty.");

await LoadScriptsAsync().AnyContext();

if (expiresIn.HasValue) {
var result = await Database.ScriptEvaluateAsync(_setIfLowerScript, new { key, value, expires = expiresIn.Value.TotalSeconds }).AnyContext();
return (long)result;
} else {
var result = await Database.ScriptEvaluateAsync(_setIfLowerScript, new { key, value, expires = RedisValue.EmptyString }).AnyContext();
return (long)result;
}
}

private Task<bool> InternalSetAsync<T>(string key, T value, TimeSpan? expiresIn = null, When when = When.Always, CommandFlags flags = CommandFlags.None) {
Expand Down Expand Up @@ -256,6 +296,24 @@ public async Task<double> IncrementAsync(string key, double amount = 1, TimeSpan
return -1;
}

if (expiresIn.HasValue) {
await LoadScriptsAsync().AnyContext();
var result = await Database.ScriptEvaluateAsync(_incrByAndExpireScript, new { key, value = amount, expires = expiresIn.Value.TotalSeconds }).AnyContext();
return (double)result;
}

return await Database.StringIncrementAsync(key, amount).AnyContext();
}

public async Task<long> IncrementAsync(string key, long amount = 1, TimeSpan? expiresIn = null) {
if (String.IsNullOrEmpty(key))
throw new ArgumentNullException(nameof(key), "Key cannot be null or empty.");

if (expiresIn?.Ticks < 0) {
await this.RemoveAsync(key).AnyContext();
return -1;
}

if (expiresIn.HasValue) {
await LoadScriptsAsync().AnyContext();
var result = await Database.ScriptEvaluateAsync(_incrByAndExpireScript, new { key, value = amount, expires = expiresIn.Value.TotalSeconds }).AnyContext();
Expand Down Expand Up @@ -331,7 +389,7 @@ public void Dispose() {
if c then
if tonumber(@value) > c then
redis.call('set', @key, @value)
if (@expires) then
if (@expires ~= nil and @expires ~= '') then
redis.call('expire', @key, math.ceil(@expires))
end
return tonumber(@value) - c
Expand All @@ -340,40 +398,40 @@ public void Dispose() {
end
else
redis.call('set', @key, @value)
if (@expires) then
if (@expires ~= nil and @expires ~= '') then
redis.call('expire', @key, math.ceil(@expires))
end
return tonumber(@value)
end";

private const string SET_IF_LOWER = @"local c = tonumber(redis.call('get', @key))
if c then
if tonumber(@value) > c then
if tonumber(@value) < c then
redis.call('set', @key, @value)
if (@expires) then
if (@expires ~= nil and @expires ~= '') then
redis.call('expire', @key, math.ceil(@expires))
end
return tonumber(@value) - c
return c - tonumber(@value)
else
return 0
end
else
redis.call('set', @key, @value)
if (@expires) then
if (@expires ~= nil and @expires ~= '') then
redis.call('expire', @key, math.ceil(@expires))
end
return tonumber(@value)
end";

private const string INCRBY_AND_EXPIRE = @"if math.modf(@value) == 0 then
local v = redis.call('incrby', @key, @value)
if (@expires) then
if (@expires ~= nil and @expires ~= '') then
redis.call('expire', @key, math.ceil(@expires))
end
return tonumber(v)
else
local v = redis.call('incrbyfloat', @key, @value)
if (@expires) then
if (@expires ~= nil and @expires ~= '') then
redis.call('expire', @key, math.ceil(@expires))
end
return tonumber(v)
Expand Down
2 changes: 1 addition & 1 deletion src/Foundatio.Redis/Foundatio.Redis.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<AssemblyOriginatorKeyFile>..\Exceptionless.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Foundatio" Version="7.0.1638-pre" />
<PackageReference Include="Foundatio" Version="7.0.1640-pre" />
<PackageReference Include="StackExchange.Redis.StrongName" Version="1.2.6" />
</ItemGroup>
</Project>
15 changes: 15 additions & 0 deletions test/Foundatio.Redis.Tests/Caching/RedisCacheClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,21 @@ public override Task CanManageSetsAsync() {
return base.CanManageSetsAsync();
}

[Fact]
public override Task CanGetAndSetDateTime() {
return base.CanGetAndSetDateTime();
}

[Fact]
public override Task CanRoundTripLargeNumbersAsync() {
return base.CanRoundTripLargeNumbersAsync();
}

[Fact]
public override Task CanRoundTripLargeNumbersWithExpirationAsync() {
return base.CanRoundTripLargeNumbersWithExpirationAsync();
}

[Fact(Skip = "Performance Test")]
public override Task MeasureThroughputAsync() {
return base.MeasureThroughputAsync();
Expand Down
2 changes: 1 addition & 1 deletion test/Foundatio.Redis.Tests/Foundatio.Redis.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<ProjectReference Include="..\..\src\Foundatio.Redis\Foundatio.Redis.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Foundatio.TestHarness" Version="7.0.1638-pre" />
<PackageReference Include="Foundatio.TestHarness" Version="7.0.1640-pre" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
Expand Down

0 comments on commit 66a1daa

Please sign in to comment.