-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support serializing session changesets
Previously, the serialization of the session state to go between the remote server and the client would deserialize/serialize every key even if it hadn't changed. As part of this change: - The session state tracks which items are new or have been accessed (and thus potentially changed) - Only sends a diff list rather than all the items - Only deserializes items if they are being accessed - Make the ISessionSerializer internal as its not the preferred way to serialize session state (use
- Loading branch information
1 parent
91322f1
commit f69ed01
Showing
13 changed files
with
954 additions
and
157 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
# Session serialization | ||
|
||
Session serialization is provided through the `ISessionSerializer` type. There are two modes that are available: | ||
|
||
## Common structure | ||
|
||
```mermaid | ||
packet-beta | ||
0: "M" | ||
1-10: "Session Id (Variable length)" | ||
11: "N" | ||
12: "A" | ||
13: "R" | ||
14: "T" | ||
15: "C" | ||
16-24: "Key 1 Blob" | ||
25-33: "Key 2 Blob" | ||
34-42: "..." | ||
43-50: "Flags (variable)" | ||
``` | ||
|
||
Where: | ||
- *M*: Mode | ||
- *N*: New session | ||
- *A*: Abandoned | ||
- *R*: Readonly | ||
- *T*: Timeout | ||
- *C*: Key count | ||
|
||
## Flags | ||
|
||
Flags allow for additional information to be sent either direction that may not be known initially. This field was added v2 but is backwards compatible with the v1 deserializer and will operate as a no-op as it just reads the things it knows about and doesn't look for the end of a payload. | ||
|
||
Structure: | ||
|
||
```mermaid | ||
packet-beta | ||
0: "C" | ||
1: "F1" | ||
2: "F1L" | ||
3-10: "Flag1 specific payload" | ||
11: "F2" | ||
12: "F2L" | ||
13-20: "Flag2 specific payload" | ||
21-25: "..." | ||
``` | ||
|
||
Where: | ||
- *Fn*: Flag `n` | ||
|
||
Where `C` is the count of flags, and each `Fn` is a flag identifier an int with 7bit encoding. Each f | ||
|
||
An example is the flag section used to indicate that there is support for diffing a session state on the server: | ||
|
||
```mermaid | ||
packet-beta | ||
0: "1" | ||
1: "100" | ||
2: "0" | ||
``` | ||
|
||
## Full Copy (Mode = 1) | ||
|
||
The following is the structure of the key blobs when the full state is serialized: | ||
|
||
```mermaid | ||
packet-beta | ||
0-10: "Key name" | ||
11-20: "Serialized value" | ||
``` | ||
|
||
## Diffing Support (Mode = 2) | ||
|
||
The following is the structure of the key blobs when only the difference is serialized: | ||
|
||
```mermaid | ||
packet-beta | ||
0-10: "Key name" | ||
11: "S" | ||
12-20: "Serialized value" | ||
``` | ||
|
||
Where: | ||
- *S*: The session state. Supported values include those defined by `SessionItemChangeState` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
...tCore.SystemWebAdapters.Abstractions/SessionState/Serialization/ISessionStateChangeset.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Collections.Generic; | ||
|
||
namespace Microsoft.AspNetCore.SystemWebAdapters.SessionState.Serialization; | ||
|
||
public interface ISessionStateChangeset : ISessionState | ||
{ | ||
IEnumerable<SessionStateChangeItem> Changes { get; } | ||
} |
13 changes: 13 additions & 0 deletions
13
...tCore.SystemWebAdapters.Abstractions/SessionState/Serialization/SessionItemChangeState.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
namespace Microsoft.AspNetCore.SystemWebAdapters.SessionState.Serialization; | ||
|
||
public enum SessionItemChangeState | ||
{ | ||
Unknown = 0, | ||
NoChange = 1, | ||
Removed = 2, | ||
Changed = 3, | ||
New = 4, | ||
} |
2 changes: 1 addition & 1 deletion
2
...ore.SystemWebAdapters.Abstractions/SessionState/Serialization/SessionSerializerOptions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
...tCore.SystemWebAdapters.Abstractions/SessionState/Serialization/SessionStateChangeItem.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Diagnostics; | ||
|
||
namespace Microsoft.AspNetCore.SystemWebAdapters.SessionState.Serialization; | ||
|
||
[DebuggerDisplay("{State}: {Key,nq}")] | ||
public readonly struct SessionStateChangeItem(SessionItemChangeState state, string key) : IEquatable<SessionStateChangeItem> | ||
{ | ||
public SessionItemChangeState State => state; | ||
|
||
public string Key => key; | ||
|
||
public override bool Equals(object? obj) => obj is SessionStateChangeItem item && Equals(item); | ||
|
||
public override int GetHashCode() | ||
=> State.GetHashCode() ^ Key.GetHashCode(); | ||
|
||
public bool Equals(SessionStateChangeItem other) => | ||
State == other.State | ||
&& string.Equals(Key, other.Key, StringComparison.Ordinal); | ||
|
||
public static bool operator ==(SessionStateChangeItem left, SessionStateChangeItem right) | ||
{ | ||
return left.Equals(right); | ||
} | ||
|
||
public static bool operator !=(SessionStateChangeItem left, SessionStateChangeItem right) | ||
{ | ||
return !(left == right); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
118 changes: 118 additions & 0 deletions
118
src/Services/SessionState/BinarySessionSerializer.ChangesetWriter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Options; | ||
|
||
namespace Microsoft.AspNetCore.SystemWebAdapters.SessionState.Serialization; | ||
|
||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Source shared with .NET Framework that does not have the method")] | ||
internal partial class BinarySessionSerializer : ISessionSerializer | ||
{ | ||
private readonly struct ChangesetWriter(ISessionKeySerializer serializer) | ||
{ | ||
public List<string>? Write(ISessionStateChangeset state, BinaryWriter writer) | ||
{ | ||
writer.Write(ModeDelta); | ||
writer.Write(state.SessionID); | ||
|
||
writer.Write(state.IsNewSession); | ||
writer.Write(state.IsAbandoned); | ||
writer.Write(state.IsReadOnly); | ||
|
||
writer.Write7BitEncodedInt(state.Timeout); | ||
writer.Write7BitEncodedInt(state.Count); | ||
|
||
List<string>? unknownKeys = null; | ||
|
||
foreach (var item in state.Changes) | ||
{ | ||
writer.Write(item.Key); | ||
|
||
// New with V2 serializer | ||
if (item.State is SessionItemChangeState.NoChange or SessionItemChangeState.Removed) | ||
{ | ||
writer.Write7BitEncodedInt((int)item.State); | ||
} | ||
else if (serializer.TrySerialize(item.Key, state[item.Key], out var result)) | ||
{ | ||
writer.Write7BitEncodedInt((int)item.State); | ||
writer.Write7BitEncodedInt(result.Length); | ||
writer.Write(result); | ||
} | ||
else | ||
{ | ||
(unknownKeys ??= []).Add(item.Key); | ||
writer.Write7BitEncodedInt((int)SessionItemChangeState.Unknown); | ||
} | ||
} | ||
|
||
writer.WriteFlags([]); | ||
|
||
return unknownKeys; | ||
} | ||
|
||
public SessionStateCollection Read(BinaryReader reader) | ||
{ | ||
var state = SessionStateCollection.CreateTracking(serializer); | ||
|
||
state.SessionID = reader.ReadString(); | ||
state.IsNewSession = reader.ReadBoolean(); | ||
state.IsAbandoned = reader.ReadBoolean(); | ||
state.IsReadOnly = reader.ReadBoolean(); | ||
state.Timeout = reader.Read7BitEncodedInt(); | ||
|
||
var count = reader.Read7BitEncodedInt(); | ||
|
||
for (var index = count; index > 0; index--) | ||
{ | ||
var key = reader.ReadString(); | ||
var changeState = (SessionItemChangeState)reader.Read7BitEncodedInt(); | ||
|
||
if (changeState is SessionItemChangeState.NoChange) | ||
{ | ||
state.MarkUnchanged(key); | ||
} | ||
else if (changeState is SessionItemChangeState.Removed) | ||
{ | ||
state.MarkRemoved(key); | ||
} | ||
else if (changeState is SessionItemChangeState.Unknown) | ||
{ | ||
state.AddUnknownKey(key); | ||
} | ||
else if (changeState is SessionItemChangeState.New or SessionItemChangeState.Changed) | ||
{ | ||
var length = reader.Read7BitEncodedInt(); | ||
var bytes = reader.ReadBytes(length); | ||
|
||
if (serializer.TryDeserialize(key, bytes, out var result)) | ||
{ | ||
if (result is not null) | ||
{ | ||
state[key] = result; | ||
} | ||
} | ||
else | ||
{ | ||
state.AddUnknownKey(key); | ||
} | ||
} | ||
} | ||
|
||
foreach (var (flag, payload) in reader.ReadFlags()) | ||
{ | ||
// No flags are currently read | ||
} | ||
|
||
return state; | ||
} | ||
} | ||
} |
Oops, something went wrong.