#167 Add cancellation tokens and defualt timeouts for Load/CommitAsync.

This commit is contained in:
Chris R 2017-06-28 15:44:43 -07:00
parent 6f4f34a0bf
commit 1a1e4c309f
6 changed files with 93 additions and 39 deletions

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
@ -24,6 +25,7 @@ namespace Microsoft.AspNetCore.Session
private readonly IDistributedCache _cache;
private readonly string _sessionKey;
private readonly TimeSpan _idleTimeout;
private readonly TimeSpan _ioTimeout;
private readonly Func<bool> _tryEstablishSession;
private readonly ILogger _logger;
private IDictionary<EncodedKey, byte[]> _store;
@ -38,6 +40,7 @@ namespace Microsoft.AspNetCore.Session
IDistributedCache cache,
string sessionKey,
TimeSpan idleTimeout,
TimeSpan ioTimeout,
Func<bool> tryEstablishSession,
ILoggerFactory loggerFactory,
bool isNewSessionKey)
@ -65,6 +68,7 @@ namespace Microsoft.AspNetCore.Session
_cache = cache;
_sessionKey = sessionKey;
_idleTimeout = idleTimeout;
_ioTimeout = ioTimeout;
_tryEstablishSession = tryEstablishSession;
_store = new Dictionary<EncodedKey, byte[]>();
_logger = loggerFactory.CreateLogger<DistributedSession>();
@ -194,58 +198,68 @@ namespace Microsoft.AspNetCore.Session
}
// This will throw if called directly and a failure occurs. The user is expected to handle the failures.
public async Task LoadAsync()
public async Task LoadAsync(CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
if (!_loaded)
{
var data = await _cache.GetAsync(_sessionKey);
if (data != null)
using (var timeout = new CancellationTokenSource(_ioTimeout))
{
Deserialize(new MemoryStream(data));
}
else if (!_isNewSessionKey)
{
_logger.AccessingExpiredSession(_sessionKey);
var cts = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancellationToken);
var data = await _cache.GetAsync(_sessionKey, cts.Token);
if (data != null)
{
Deserialize(new MemoryStream(data));
}
else if (!_isNewSessionKey)
{
_logger.AccessingExpiredSession(_sessionKey);
}
}
_isAvailable = true;
_loaded = true;
}
}
public async Task CommitAsync()
public async Task CommitAsync(CancellationToken cancellationToken = default(CancellationToken))
{
if (_isModified)
cancellationToken.ThrowIfCancellationRequested();
using (var timeout = new CancellationTokenSource(_ioTimeout))
{
if (_logger.IsEnabled(LogLevel.Information))
var cts = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancellationToken);
if (_isModified)
{
try
if (_logger.IsEnabled(LogLevel.Information))
{
var data = await _cache.GetAsync(_sessionKey);
if (data == null)
try
{
_logger.SessionStarted(_sessionKey, Id);
var data = await _cache.GetAsync(_sessionKey, cts.Token);
if (data == null)
{
_logger.SessionStarted(_sessionKey, Id);
}
}
catch (Exception exception)
{
_logger.SessionCacheReadException(_sessionKey, exception);
}
}
catch (Exception exception)
{
_logger.SessionCacheReadException(_sessionKey, exception);
}
var stream = new MemoryStream();
Serialize(stream);
await _cache.SetAsync(
_sessionKey,
stream.ToArray(),
new DistributedCacheEntryOptions().SetSlidingExpiration(_idleTimeout),
cts.Token);
_isModified = false;
_logger.SessionStored(_sessionKey, Id, _store.Count);
}
else
{
await _cache.RefreshAsync(_sessionKey, cts.Token);
}
var stream = new MemoryStream();
Serialize(stream);
await _cache.SetAsync(
_sessionKey,
stream.ToArray(),
new DistributedCacheEntryOptions().SetSlidingExpiration(_idleTimeout));
_isModified = false;
_logger.SessionStored(_sessionKey, Id, _store.Count);
}
else
{
await _cache.RefreshAsync(_sessionKey);
}
}

View File

@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Session
_loggerFactory = loggerFactory;
}
public ISession Create(string sessionKey, TimeSpan idleTimeout, Func<bool> tryEstablishSession, bool isNewSessionKey)
public ISession Create(string sessionKey, TimeSpan idleTimeout, TimeSpan ioTimeout, Func<bool> tryEstablishSession, bool isNewSessionKey)
{
if (string.IsNullOrEmpty(sessionKey))
{
@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Session
throw new ArgumentNullException(nameof(tryEstablishSession));
}
return new DistributedSession(_cache, sessionKey, idleTimeout, tryEstablishSession, _loggerFactory, isNewSessionKey);
return new DistributedSession(_cache, sessionKey, idleTimeout, ioTimeout, tryEstablishSession, _loggerFactory, isNewSessionKey);
}
}
}

View File

@ -8,6 +8,6 @@ namespace Microsoft.AspNetCore.Session
{
public interface ISessionStore
{
ISession Create(string sessionKey, TimeSpan idleTimeout, Func<bool> tryEstablishSession, bool isNewSessionKey);
ISession Create(string sessionKey, TimeSpan idleTimeout, TimeSpan ioTimeout, Func<bool> tryEstablishSession, bool isNewSessionKey);
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
@ -98,7 +99,7 @@ namespace Microsoft.AspNetCore.Session
}
var feature = new SessionFeature();
feature.Session = _sessionStore.Create(sessionKey, _options.IdleTimeout, tryEstablishSession, isNewSessionKey);
feature.Session = _sessionStore.Create(sessionKey, _options.IdleTimeout, _options.IOTimeout, tryEstablishSession, isNewSessionKey);
context.Features.Set<ISessionFeature>(feature);
try
@ -113,7 +114,7 @@ namespace Microsoft.AspNetCore.Session
{
try
{
await feature.Session.CommitAsync();
await feature.Session.CommitAsync(context.RequestAborted);
}
catch (Exception ex)
{

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Session;
@ -35,6 +36,12 @@ namespace Microsoft.AspNetCore.Builder
/// </summary>
public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromMinutes(20);
/// <summary>
/// The maximim amount of time allowed to load a session from the store or to commit it back to the store.
/// Note this may only apply to asynchronous operations. This timeout can be disabled using <see cref="Timeout.InfiniteTimeSpan"/>.
/// </summary>
public TimeSpan IOTimeout { get; set; } = TimeSpan.FromMinutes(1);
#region Obsolete API
/// <summary>
/// <para>

View File

@ -0,0 +1,32 @@
[
{
"TypeId": "public interface Microsoft.AspNetCore.Session.ISessionStore",
"MemberId": "Microsoft.AspNetCore.Http.ISession Create(System.String sessionKey, System.TimeSpan idleTimeout, System.Func<System.Boolean> tryEstablishSession, System.Boolean isNewSessionKey)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Session.DistributedSession : Microsoft.AspNetCore.Http.ISession",
"MemberId": "public .ctor(Microsoft.Extensions.Caching.Distributed.IDistributedCache cache, System.String sessionKey, System.TimeSpan idleTimeout, System.Func<System.Boolean> tryEstablishSession, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, System.Boolean isNewSessionKey)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Session.DistributedSession : Microsoft.AspNetCore.Http.ISession",
"MemberId": "public System.Threading.Tasks.Task CommitAsync()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Session.DistributedSession : Microsoft.AspNetCore.Http.ISession",
"MemberId": "public System.Threading.Tasks.Task LoadAsync()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Session.DistributedSessionStore : Microsoft.AspNetCore.Session.ISessionStore",
"MemberId": "public Microsoft.AspNetCore.Http.ISession Create(System.String sessionKey, System.TimeSpan idleTimeout, System.Func<System.Boolean> tryEstablishSession, System.Boolean isNewSessionKey)",
"Kind": "Removal"
},
{
"TypeId": "public interface Microsoft.AspNetCore.Session.ISessionStore",
"MemberId": "Microsoft.AspNetCore.Http.ISession Create(System.String sessionKey, System.TimeSpan idleTimeout, System.TimeSpan ioTimeout, System.Func<System.Boolean> tryEstablishSession, System.Boolean isNewSessionKey)",
"Kind": "Addition"
}
]