You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@geode.apache.org by GitBox <gi...@apache.org> on 2021/07/28 05:59:53 UTC

[GitHub] [geode-native] mmartell opened a new pull request #834: GEODE-9359: Add NetCore SessionState support

mmartell opened a new pull request #834:
URL: https://github.com/apache/geode-native/pull/834


   This PR brings in the NetCore-Session code.
   
   Note: There will be a separate PR for adding CI support for NetCore-Session under GEODE-9357.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679278944



##########
File path: netcore/geode-dotnet-core.sln
##########
@@ -7,24 +7,56 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore", "NetCore\NetCore.
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Test", "NetCore.Test\NetCore.Test.csproj", "{501DEA7E-8985-42A8-8BC9-C073E1B6DFE0}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Session", "Session", "{520C96EC-F929-4365-8D78-CC5785419B62}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Session", "NetCore.Session\NetCore.Session.csproj", "{B88C58EB-B144-403B-85F7-7A5B45E643E3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Session.IntegrationTests", "NetCore.Session.IntegrationTests\NetCore.Session.IntegrationTests.csproj", "{94D2CD59-A5F3-4504-BF01-0A3B95CE12B5}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
 		Debug|x64 = Debug|x64
+		Release|Any CPU = Release|Any CPU
 		Release|x64 = Release|x64
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Debug|Any CPU.ActiveCfg = Debug|x64
 		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Debug|x64.ActiveCfg = Debug|x64
 		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Debug|x64.Build.0 = Debug|x64
+		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Release|Any CPU.ActiveCfg = Release|x64

Review comment:
       Might need to be in the .csproj and not in the solution:
   ```
     <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
       <DebugType>pdbonly</DebugType>
       <DebugSymbols>true</DebugSymbols>
     </PropertyGroup>```
   




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679496018



##########
File path: netcore/NetCore.Session.IntegrationTests/NetCore.Session.IntegrationTests.csproj
##########
@@ -0,0 +1,26 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
+
+    <IsPackable>false</IsPackable>
+
+    <Platforms>x64</Platforms>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
+    <PackageReference Include="xunit" Version="2.4.1" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
+      <PrivateAssets>all</PrivateAssets>

Review comment:
       Not sure what you're asking here. Are you wondering why xunit.runner.visualstudio is at 2.4.3 and xunit is at 2.4.1? If so, 2.4.1 is the latest version of xunit on  NuGet.org.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679224313



##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value
+        {
+            get { return _value; }
+            set { _value = value; }
+        }
+        public DateTime LastAccessTimeUtc
+        {
+            get { return _lastAccessTimeUtc; }
+            set { _lastAccessTimeUtc = value; }
+        }
+
+        public DateTime ExpirationTimeUtc
+        {
+            get { return _expirationTimeUtc; }
+            set { _expirationTimeUtc = value; }
+        }
+
+        public TimeSpan SpanUntilStale
+        {
+            get { return _spanUntilStale; }
+            set { _spanUntilStale = value; }
+        }
+
+        public byte[] ToByteArray()
+        {
+            int neededBytes = 3 * sizeof(long) + _value.Length;
+            byte[] byteArray = new byte[neededBytes];
+            int byteIndex = 0;
+
+            // Append LastAccessTimeUtc
+            Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append ExpirationTimeUtc
+            Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append SpanUntilStale
+            Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append the value
+            Array.Copy(_value, 0, byteArray, byteIndex, _value.Length);
+            return byteArray;
+        }
+
+        public void FromByteArray(byte[] data)
+        {
+            int byteIndex = 0;
+
+            // Extract the LastAccessTimeUtc
+            LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the ExpirationTimeUtc
+            ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the SpanUntilStale
+            SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the value
+            Value = new byte[data.Length - byteIndex];
+            Array.Copy(data, byteIndex, _value, 0, data.Length - byteIndex);
+        }
+    }
+
+    public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache
+    {
+        private readonly IGeodeCache _cache;
+        private ILogger<GeodeSessionStateCache> _logger;
+        private static Region _region;
+        private string _regionName;
+        private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+        public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+
+            var host = optionsAccessor.Value.Host;
+            var port = optionsAccessor.Value.Port;
+            _regionName = optionsAccessor.Value.RegionName;
+
+            _cache = CacheFactory.Create()
+                .SetProperty("log-level", "none")
+                .CreateCache();
+
+            _cache.PoolManager
+                .CreatePoolFactory()
+                .AddLocator(host, port)
+                .CreatePool("pool");
+
+            var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+            _region = regionFactory.CreateRegion(_regionName);
+        }
+
+        // Returns the SessionStateValue for key, or null if key doesn't exist
+        public GeodeSessionStateValue GetValueForKey(string key)
+        {
+            byte[] cacheValue = _region.GetByteArray(key);
+
+            if (cacheValue != null)
+            {
+                return new GeodeSessionStateValue(cacheValue);
+            }
+            else
+                return null;
+        }
+
+        public byte[] Get(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Check for nonexistent key
+            GeodeSessionStateValue ssValue = GetValueForKey(key);
+            if (ssValue == null)

Review comment:
       Done.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679181856



##########
File path: netcore/NetCore.Session/GeodeCacheServiceCollectionExtensions.cs
##########
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ using System;

Review comment:
       Done




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679497384



##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session {
+  public class GeodeSessionStateValue {
+    //DateTime _lastAccessTimeUtc;

Review comment:
       Good catch.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679227590



##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value
+        {
+            get { return _value; }
+            set { _value = value; }
+        }
+        public DateTime LastAccessTimeUtc
+        {
+            get { return _lastAccessTimeUtc; }
+            set { _lastAccessTimeUtc = value; }
+        }
+
+        public DateTime ExpirationTimeUtc
+        {
+            get { return _expirationTimeUtc; }
+            set { _expirationTimeUtc = value; }
+        }
+
+        public TimeSpan SpanUntilStale
+        {
+            get { return _spanUntilStale; }
+            set { _spanUntilStale = value; }
+        }
+
+        public byte[] ToByteArray()
+        {
+            int neededBytes = 3 * sizeof(long) + _value.Length;
+            byte[] byteArray = new byte[neededBytes];
+            int byteIndex = 0;
+
+            // Append LastAccessTimeUtc
+            Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append ExpirationTimeUtc
+            Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append SpanUntilStale
+            Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append the value
+            Array.Copy(_value, 0, byteArray, byteIndex, _value.Length);
+            return byteArray;
+        }
+
+        public void FromByteArray(byte[] data)
+        {
+            int byteIndex = 0;
+
+            // Extract the LastAccessTimeUtc
+            LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the ExpirationTimeUtc
+            ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the SpanUntilStale
+            SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the value
+            Value = new byte[data.Length - byteIndex];
+            Array.Copy(data, byteIndex, _value, 0, data.Length - byteIndex);
+        }
+    }
+
+    public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache
+    {
+        private readonly IGeodeCache _cache;
+        private ILogger<GeodeSessionStateCache> _logger;
+        private static Region _region;
+        private string _regionName;
+        private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+        public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+
+            var host = optionsAccessor.Value.Host;
+            var port = optionsAccessor.Value.Port;
+            _regionName = optionsAccessor.Value.RegionName;
+
+            _cache = CacheFactory.Create()
+                .SetProperty("log-level", "none")
+                .CreateCache();
+
+            _cache.PoolManager
+                .CreatePoolFactory()
+                .AddLocator(host, port)
+                .CreatePool("pool");
+
+            var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+            _region = regionFactory.CreateRegion(_regionName);
+        }
+
+        // Returns the SessionStateValue for key, or null if key doesn't exist
+        public GeodeSessionStateValue GetValueForKey(string key)
+        {
+            byte[] cacheValue = _region.GetByteArray(key);
+
+            if (cacheValue != null)
+            {
+                return new GeodeSessionStateValue(cacheValue);
+            }
+            else
+                return null;
+        }
+
+        public byte[] Get(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Check for nonexistent key
+            GeodeSessionStateValue ssValue = GetValueForKey(key);
+            if (ssValue == null)
+                return null;
+
+            // Check for expired key
+            DateTime nowUtc = DateTime.UtcNow;
+            if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc)
+                return null;
+
+            // Check for stale key
+            if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+              nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale))
+                return null;
+
+            //LogDebug("Inserting against key [" + key + "] with absolute expiration: " +
+            //         options.AbsoluteExpiration.Value.DateTime);
+
+            // Update the times for sliding expirations
+            if (ssValue.SpanUntilStale != TimeSpan.Zero)
+            {
+                ssValue.LastAccessTimeUtc = nowUtc;
+                _region.PutByteArray(key, ssValue.ToByteArray());
+            }
+
+            return ssValue.Value;
+        }
+
+        public Task<byte[]> GetAsync(string key, CancellationToken token = default(CancellationToken))
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            token.ThrowIfCancellationRequested();
+
+            return Task.Factory.StartNew(() => Get(key), token);
+        }
+
+        public void Refresh(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Check for nonexistent key
+            GeodeSessionStateValue ssValue = GetValueForKey(key);
+            if (ssValue == null)
+                return;
+
+            // Check for expired key
+            DateTime nowUtc = DateTime.UtcNow;
+            if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc)
+                return;
+
+            // Check for stale key
+            if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+              nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale))
+                return;
+
+            //LogDebug("Inserting against key [" + key + "] with absolute expiration: " +

Review comment:
       Done




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] pivotal-jbarrett commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
pivotal-jbarrett commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r680154554



##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session {
+  public class GeodeSessionStateValue {
+    public GeodeSessionStateValue() {}
+    public GeodeSessionStateValue(byte[] value) {
+      FromByteArray(value);
+    }
+
+    public byte[] Value { get; set; }
+    public DateTime LastAccessTimeUtc { get; set; }
+    public DateTime ExpirationTimeUtc { get; set; } = DateTime.MinValue;
+    public TimeSpan SpanUntilStale { get; set; } = TimeSpan.Zero;
+
+    public byte[] ToByteArray() {
+      int neededBytes = 3 * sizeof(long) + Value.Length;
+      byte[] byteArray = new byte[neededBytes];
+      int byteIndex = 0;
+
+      Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(Value, 0, byteArray, byteIndex, Value.Length);
+      return byteArray;
+    }
+
+    public void FromByteArray(byte[] data) {
+      int byteIndex = 0;
+
+      LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      Value = new byte[data.Length - byteIndex];
+      Array.Copy(data, byteIndex, Value, 0, data.Length - byteIndex);
+    }
+  }
+
+  public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache {
+    private readonly IGeodeCache _cache;
+    private ILogger<GeodeSessionStateCache> _logger;
+    private static Region _region;
+    private string _logLevel;
+    private string _logFile;
+    private string _regionName;
+    private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+    public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+      var host = optionsAccessor.Value.Host;
+      var port = optionsAccessor.Value.Port;
+      _regionName = optionsAccessor.Value.RegionName;
+      _logLevel = optionsAccessor.Value.LogLevel;
+      _logFile = optionsAccessor.Value.LogFile;
+
+      _cache = CacheFactory.Create()
+                   .SetProperty("log-level", _logLevel)
+                   .SetProperty("log-file", _logFile)
+                   .CreateCache();
+
+      _cache.PoolManager.CreatePoolFactory().AddLocator(host, port).CreatePool("pool");
+
+      var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+      _region = regionFactory.CreateRegion(_regionName);
+    }
+
+    // Returns the SessionStateValue for key, or null if key doesn't exist
+    public GeodeSessionStateValue GetValueForKey(string key) {
+      var cacheValue = _region.GetByteArray(key);
+
+      if (cacheValue != null) {
+        return new GeodeSessionStateValue(cacheValue);
+      } else
+        return null;
+    }
+
+    public byte[] Get(string key) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      Connect();
+
+      // Check for nonexistent key
+      GeodeSessionStateValue ssValue = GetValueForKey(key);

Review comment:
       `var`

##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session {
+  public class GeodeSessionStateValue {
+    public GeodeSessionStateValue() {}
+    public GeodeSessionStateValue(byte[] value) {
+      FromByteArray(value);
+    }
+
+    public byte[] Value { get; set; }
+    public DateTime LastAccessTimeUtc { get; set; }
+    public DateTime ExpirationTimeUtc { get; set; } = DateTime.MinValue;
+    public TimeSpan SpanUntilStale { get; set; } = TimeSpan.Zero;
+
+    public byte[] ToByteArray() {
+      int neededBytes = 3 * sizeof(long) + Value.Length;
+      byte[] byteArray = new byte[neededBytes];
+      int byteIndex = 0;
+
+      Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(Value, 0, byteArray, byteIndex, Value.Length);
+      return byteArray;
+    }
+
+    public void FromByteArray(byte[] data) {
+      int byteIndex = 0;
+
+      LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      Value = new byte[data.Length - byteIndex];
+      Array.Copy(data, byteIndex, Value, 0, data.Length - byteIndex);
+    }
+  }
+
+  public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache {
+    private readonly IGeodeCache _cache;
+    private ILogger<GeodeSessionStateCache> _logger;
+    private static Region _region;
+    private string _logLevel;
+    private string _logFile;
+    private string _regionName;
+    private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+    public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+      var host = optionsAccessor.Value.Host;
+      var port = optionsAccessor.Value.Port;
+      _regionName = optionsAccessor.Value.RegionName;
+      _logLevel = optionsAccessor.Value.LogLevel;
+      _logFile = optionsAccessor.Value.LogFile;
+
+      _cache = CacheFactory.Create()
+                   .SetProperty("log-level", _logLevel)
+                   .SetProperty("log-file", _logFile)
+                   .CreateCache();
+
+      _cache.PoolManager.CreatePoolFactory().AddLocator(host, port).CreatePool("pool");
+
+      var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+      _region = regionFactory.CreateRegion(_regionName);
+    }
+
+    // Returns the SessionStateValue for key, or null if key doesn't exist
+    public GeodeSessionStateValue GetValueForKey(string key) {
+      var cacheValue = _region.GetByteArray(key);
+
+      if (cacheValue != null) {
+        return new GeodeSessionStateValue(cacheValue);
+      } else
+        return null;
+    }
+
+    public byte[] Get(string key) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      Connect();
+
+      // Check for nonexistent key
+      GeodeSessionStateValue ssValue = GetValueForKey(key);
+      if (ssValue == null) {
+        return null;
+      }
+
+      // Check for expired key
+      DateTime nowUtc = DateTime.UtcNow;

Review comment:
       Still a few more local variables that should be `var`.

##########
File path: netcore/NetCore.Session.IntegrationTests/NetCore.Session.IntegrationTests.csproj
##########
@@ -0,0 +1,23 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
+
+    <IsPackable>false</IsPackable>
+
+    <Platforms>x64</Platforms>
+  </PropertyGroup>
+
+  <ItemGroup>
+	<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />

Review comment:
       Inconsistent indentations.

##########
File path: netcore/geode-dotnet-core.sln
##########
@@ -7,24 +7,56 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore", "NetCore\NetCore.
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Test", "NetCore.Test\NetCore.Test.csproj", "{501DEA7E-8985-42A8-8BC9-C073E1B6DFE0}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Session", "Session", "{520C96EC-F929-4365-8D78-CC5785419B62}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Session", "NetCore.Session\NetCore.Session.csproj", "{B88C58EB-B144-403B-85F7-7A5B45E643E3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Session.IntegrationTests", "NetCore.Session.IntegrationTests\NetCore.Session.IntegrationTests.csproj", "{94D2CD59-A5F3-4504-BF01-0A3B95CE12B5}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
 		Debug|x64 = Debug|x64
+		Release|Any CPU = Release|Any CPU
 		Release|x64 = Release|x64
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Debug|Any CPU.ActiveCfg = Debug|x64

Review comment:
       You still need to remove the `Any CPU`




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679486286



##########
File path: netcore/NetCore.Session.IntegrationTests/NetCore.Session.IntegrationTests.csproj
##########
@@ -0,0 +1,26 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
+
+    <IsPackable>false</IsPackable>
+
+    <Platforms>x64</Platforms>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />

Review comment:
       Looks like this is needed for running tests, both from Visual Studio and the command line via dotnet test.
   




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679194256



##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value
+        {
+            get { return _value; }
+            set { _value = value; }
+        }
+        public DateTime LastAccessTimeUtc
+        {
+            get { return _lastAccessTimeUtc; }
+            set { _lastAccessTimeUtc = value; }
+        }
+
+        public DateTime ExpirationTimeUtc
+        {
+            get { return _expirationTimeUtc; }
+            set { _expirationTimeUtc = value; }
+        }
+
+        public TimeSpan SpanUntilStale
+        {
+            get { return _spanUntilStale; }
+            set { _spanUntilStale = value; }
+        }
+
+        public byte[] ToByteArray()
+        {
+            int neededBytes = 3 * sizeof(long) + _value.Length;
+            byte[] byteArray = new byte[neededBytes];
+            int byteIndex = 0;
+
+            // Append LastAccessTimeUtc

Review comment:
       Agreed. Comments are a liability, just like code.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] pivotal-jbarrett commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
pivotal-jbarrett commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r678530667



##########
File path: netcore/NetCore.Session/GeodeCacheServiceCollectionExtensions.cs
##########
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ using System;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Apache.Geode.Session
+{
+    public static class GeodeCacheServiceCollectionExtensions
+    {

Review comment:
       Formatting is not consistent with the .NET Core client sources.

##########
File path: netcore/NetCore.Session/GeodeCacheServiceCollectionExtensions.cs
##########
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ using System;

Review comment:
       formatting

##########
File path: netcore/geode-dotnet-core.sln
##########
@@ -7,24 +7,56 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore", "NetCore\NetCore.
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Test", "NetCore.Test\NetCore.Test.csproj", "{501DEA7E-8985-42A8-8BC9-C073E1B6DFE0}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Session", "Session", "{520C96EC-F929-4365-8D78-CC5785419B62}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Session", "NetCore.Session\NetCore.Session.csproj", "{B88C58EB-B144-403B-85F7-7A5B45E643E3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Session.IntegrationTests", "NetCore.Session.IntegrationTests\NetCore.Session.IntegrationTests.csproj", "{94D2CD59-A5F3-4504-BF01-0A3B95CE12B5}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
 		Debug|x64 = Debug|x64
+		Release|Any CPU = Release|Any CPU
 		Release|x64 = Release|x64
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Debug|Any CPU.ActiveCfg = Debug|x64
 		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Debug|x64.ActiveCfg = Debug|x64
 		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Debug|x64.Build.0 = Debug|x64
+		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Release|Any CPU.ActiveCfg = Release|x64

Review comment:
       What about `RelWithDebInfo`?

##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value
+        {
+            get { return _value; }
+            set { _value = value; }
+        }
+        public DateTime LastAccessTimeUtc
+        {
+            get { return _lastAccessTimeUtc; }
+            set { _lastAccessTimeUtc = value; }
+        }
+
+        public DateTime ExpirationTimeUtc
+        {
+            get { return _expirationTimeUtc; }
+            set { _expirationTimeUtc = value; }
+        }
+
+        public TimeSpan SpanUntilStale
+        {
+            get { return _spanUntilStale; }
+            set { _spanUntilStale = value; }
+        }
+
+        public byte[] ToByteArray()
+        {
+            int neededBytes = 3 * sizeof(long) + _value.Length;
+            byte[] byteArray = new byte[neededBytes];
+            int byteIndex = 0;
+
+            // Append LastAccessTimeUtc
+            Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append ExpirationTimeUtc
+            Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append SpanUntilStale
+            Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append the value
+            Array.Copy(_value, 0, byteArray, byteIndex, _value.Length);
+            return byteArray;
+        }
+
+        public void FromByteArray(byte[] data)
+        {
+            int byteIndex = 0;
+
+            // Extract the LastAccessTimeUtc
+            LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the ExpirationTimeUtc
+            ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the SpanUntilStale
+            SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the value
+            Value = new byte[data.Length - byteIndex];
+            Array.Copy(data, byteIndex, _value, 0, data.Length - byteIndex);
+        }
+    }
+
+    public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache
+    {
+        private readonly IGeodeCache _cache;
+        private ILogger<GeodeSessionStateCache> _logger;
+        private static Region _region;
+        private string _regionName;
+        private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+        public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+
+            var host = optionsAccessor.Value.Host;
+            var port = optionsAccessor.Value.Port;
+            _regionName = optionsAccessor.Value.RegionName;
+
+            _cache = CacheFactory.Create()
+                .SetProperty("log-level", "none")
+                .CreateCache();
+
+            _cache.PoolManager
+                .CreatePoolFactory()
+                .AddLocator(host, port)
+                .CreatePool("pool");
+
+            var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+            _region = regionFactory.CreateRegion(_regionName);
+        }
+
+        // Returns the SessionStateValue for key, or null if key doesn't exist
+        public GeodeSessionStateValue GetValueForKey(string key)
+        {
+            byte[] cacheValue = _region.GetByteArray(key);
+
+            if (cacheValue != null)
+            {
+                return new GeodeSessionStateValue(cacheValue);
+            }
+            else
+                return null;
+        }
+
+        public byte[] Get(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Check for nonexistent key
+            GeodeSessionStateValue ssValue = GetValueForKey(key);
+            if (ssValue == null)
+                return null;
+
+            // Check for expired key
+            DateTime nowUtc = DateTime.UtcNow;

Review comment:
       Use `var` throughout rather than redundantly specifying the type. Consistency with the client source is key.

##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value
+        {
+            get { return _value; }
+            set { _value = value; }
+        }
+        public DateTime LastAccessTimeUtc
+        {
+            get { return _lastAccessTimeUtc; }
+            set { _lastAccessTimeUtc = value; }
+        }
+
+        public DateTime ExpirationTimeUtc
+        {
+            get { return _expirationTimeUtc; }
+            set { _expirationTimeUtc = value; }
+        }
+
+        public TimeSpan SpanUntilStale
+        {
+            get { return _spanUntilStale; }
+            set { _spanUntilStale = value; }
+        }
+
+        public byte[] ToByteArray()
+        {
+            int neededBytes = 3 * sizeof(long) + _value.Length;
+            byte[] byteArray = new byte[neededBytes];
+            int byteIndex = 0;
+
+            // Append LastAccessTimeUtc
+            Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append ExpirationTimeUtc
+            Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append SpanUntilStale
+            Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append the value
+            Array.Copy(_value, 0, byteArray, byteIndex, _value.Length);
+            return byteArray;
+        }
+
+        public void FromByteArray(byte[] data)
+        {
+            int byteIndex = 0;
+
+            // Extract the LastAccessTimeUtc
+            LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the ExpirationTimeUtc
+            ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the SpanUntilStale
+            SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the value
+            Value = new byte[data.Length - byteIndex];
+            Array.Copy(data, byteIndex, _value, 0, data.Length - byteIndex);
+        }
+    }
+
+    public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache
+    {
+        private readonly IGeodeCache _cache;
+        private ILogger<GeodeSessionStateCache> _logger;
+        private static Region _region;
+        private string _regionName;
+        private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+        public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+
+            var host = optionsAccessor.Value.Host;
+            var port = optionsAccessor.Value.Port;
+            _regionName = optionsAccessor.Value.RegionName;
+
+            _cache = CacheFactory.Create()
+                .SetProperty("log-level", "none")

Review comment:
       Why not allow logging level to be configured?

##########
File path: netcore/geode-dotnet-core.sln
##########
@@ -7,24 +7,56 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore", "NetCore\NetCore.
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Test", "NetCore.Test\NetCore.Test.csproj", "{501DEA7E-8985-42A8-8BC9-C073E1B6DFE0}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Session", "Session", "{520C96EC-F929-4365-8D78-CC5785419B62}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Session", "NetCore.Session\NetCore.Session.csproj", "{B88C58EB-B144-403B-85F7-7A5B45E643E3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Session.IntegrationTests", "NetCore.Session.IntegrationTests\NetCore.Session.IntegrationTests.csproj", "{94D2CD59-A5F3-4504-BF01-0A3B95CE12B5}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
 		Debug|x64 = Debug|x64
+		Release|Any CPU = Release|Any CPU
 		Release|x64 = Release|x64
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Debug|Any CPU.ActiveCfg = Debug|x64

Review comment:
       Can you mix `Any CPU` with client assemblies that rely on native libraries compiled for specific CPU types?

##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value
+        {
+            get { return _value; }
+            set { _value = value; }
+        }
+        public DateTime LastAccessTimeUtc
+        {
+            get { return _lastAccessTimeUtc; }
+            set { _lastAccessTimeUtc = value; }
+        }
+
+        public DateTime ExpirationTimeUtc
+        {
+            get { return _expirationTimeUtc; }
+            set { _expirationTimeUtc = value; }
+        }
+
+        public TimeSpan SpanUntilStale
+        {
+            get { return _spanUntilStale; }
+            set { _spanUntilStale = value; }
+        }
+
+        public byte[] ToByteArray()
+        {
+            int neededBytes = 3 * sizeof(long) + _value.Length;
+            byte[] byteArray = new byte[neededBytes];
+            int byteIndex = 0;
+
+            // Append LastAccessTimeUtc
+            Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append ExpirationTimeUtc
+            Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append SpanUntilStale
+            Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append the value
+            Array.Copy(_value, 0, byteArray, byteIndex, _value.Length);
+            return byteArray;
+        }
+
+        public void FromByteArray(byte[] data)
+        {
+            int byteIndex = 0;
+
+            // Extract the LastAccessTimeUtc
+            LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the ExpirationTimeUtc
+            ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the SpanUntilStale
+            SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the value
+            Value = new byte[data.Length - byteIndex];
+            Array.Copy(data, byteIndex, _value, 0, data.Length - byteIndex);
+        }
+    }
+
+    public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache
+    {
+        private readonly IGeodeCache _cache;
+        private ILogger<GeodeSessionStateCache> _logger;
+        private static Region _region;
+        private string _regionName;
+        private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+        public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+
+            var host = optionsAccessor.Value.Host;
+            var port = optionsAccessor.Value.Port;
+            _regionName = optionsAccessor.Value.RegionName;
+
+            _cache = CacheFactory.Create()
+                .SetProperty("log-level", "none")
+                .CreateCache();
+
+            _cache.PoolManager
+                .CreatePoolFactory()
+                .AddLocator(host, port)
+                .CreatePool("pool");
+
+            var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+            _region = regionFactory.CreateRegion(_regionName);
+        }
+
+        // Returns the SessionStateValue for key, or null if key doesn't exist
+        public GeodeSessionStateValue GetValueForKey(string key)
+        {
+            byte[] cacheValue = _region.GetByteArray(key);
+
+            if (cacheValue != null)
+            {
+                return new GeodeSessionStateValue(cacheValue);
+            }
+            else
+                return null;
+        }
+
+        public byte[] Get(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Check for nonexistent key
+            GeodeSessionStateValue ssValue = GetValueForKey(key);
+            if (ssValue == null)

Review comment:
       Always use braces.

##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value
+        {
+            get { return _value; }
+            set { _value = value; }
+        }
+        public DateTime LastAccessTimeUtc
+        {
+            get { return _lastAccessTimeUtc; }
+            set { _lastAccessTimeUtc = value; }
+        }
+
+        public DateTime ExpirationTimeUtc
+        {
+            get { return _expirationTimeUtc; }
+            set { _expirationTimeUtc = value; }
+        }
+
+        public TimeSpan SpanUntilStale
+        {
+            get { return _spanUntilStale; }
+            set { _spanUntilStale = value; }
+        }
+
+        public byte[] ToByteArray()
+        {
+            int neededBytes = 3 * sizeof(long) + _value.Length;
+            byte[] byteArray = new byte[neededBytes];
+            int byteIndex = 0;
+
+            // Append LastAccessTimeUtc
+            Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append ExpirationTimeUtc
+            Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append SpanUntilStale
+            Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append the value
+            Array.Copy(_value, 0, byteArray, byteIndex, _value.Length);
+            return byteArray;
+        }
+
+        public void FromByteArray(byte[] data)
+        {
+            int byteIndex = 0;
+
+            // Extract the LastAccessTimeUtc
+            LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the ExpirationTimeUtc
+            ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the SpanUntilStale
+            SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the value
+            Value = new byte[data.Length - byteIndex];
+            Array.Copy(data, byteIndex, _value, 0, data.Length - byteIndex);
+        }
+    }
+
+    public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache
+    {
+        private readonly IGeodeCache _cache;
+        private ILogger<GeodeSessionStateCache> _logger;
+        private static Region _region;
+        private string _regionName;
+        private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+        public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+
+            var host = optionsAccessor.Value.Host;
+            var port = optionsAccessor.Value.Port;
+            _regionName = optionsAccessor.Value.RegionName;
+
+            _cache = CacheFactory.Create()
+                .SetProperty("log-level", "none")
+                .CreateCache();
+
+            _cache.PoolManager
+                .CreatePoolFactory()
+                .AddLocator(host, port)
+                .CreatePool("pool");
+
+            var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+            _region = regionFactory.CreateRegion(_regionName);
+        }
+
+        // Returns the SessionStateValue for key, or null if key doesn't exist
+        public GeodeSessionStateValue GetValueForKey(string key)
+        {
+            byte[] cacheValue = _region.GetByteArray(key);
+
+            if (cacheValue != null)
+            {
+                return new GeodeSessionStateValue(cacheValue);
+            }
+            else
+                return null;
+        }
+
+        public byte[] Get(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Check for nonexistent key
+            GeodeSessionStateValue ssValue = GetValueForKey(key);
+            if (ssValue == null)
+                return null;
+
+            // Check for expired key
+            DateTime nowUtc = DateTime.UtcNow;
+            if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc)
+                return null;
+
+            // Check for stale key
+            if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+              nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale))
+                return null;
+
+            //LogDebug("Inserting against key [" + key + "] with absolute expiration: " +
+            //         options.AbsoluteExpiration.Value.DateTime);
+
+            // Update the times for sliding expirations
+            if (ssValue.SpanUntilStale != TimeSpan.Zero)
+            {
+                ssValue.LastAccessTimeUtc = nowUtc;
+                _region.PutByteArray(key, ssValue.ToByteArray());
+            }
+
+            return ssValue.Value;
+        }
+
+        public Task<byte[]> GetAsync(string key, CancellationToken token = default(CancellationToken))
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            token.ThrowIfCancellationRequested();
+
+            return Task.Factory.StartNew(() => Get(key), token);
+        }
+
+        public void Refresh(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Check for nonexistent key
+            GeodeSessionStateValue ssValue = GetValueForKey(key);
+            if (ssValue == null)
+                return;
+
+            // Check for expired key
+            DateTime nowUtc = DateTime.UtcNow;
+            if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc)
+                return;
+
+            // Check for stale key
+            if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+              nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale))
+                return;
+
+            //LogDebug("Inserting against key [" + key + "] with absolute expiration: " +

Review comment:
       Delete commented out code.

##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value
+        {
+            get { return _value; }
+            set { _value = value; }
+        }
+        public DateTime LastAccessTimeUtc
+        {
+            get { return _lastAccessTimeUtc; }
+            set { _lastAccessTimeUtc = value; }
+        }
+
+        public DateTime ExpirationTimeUtc
+        {
+            get { return _expirationTimeUtc; }
+            set { _expirationTimeUtc = value; }
+        }
+
+        public TimeSpan SpanUntilStale
+        {
+            get { return _spanUntilStale; }
+            set { _spanUntilStale = value; }
+        }
+
+        public byte[] ToByteArray()
+        {
+            int neededBytes = 3 * sizeof(long) + _value.Length;
+            byte[] byteArray = new byte[neededBytes];
+            int byteIndex = 0;
+
+            // Append LastAccessTimeUtc

Review comment:
       These comments do provide any meaningful information over the source below it. If the intent is to show some form of segmentation in the serialization then perhaps making them full blown methods.
   ```C#
   byteIndex += AppendLastAccessTimeUtc(byteArray, byteIndex);
   byteIndex += AppendExpirationTimeUtc(byteArray, byteIndex);
   ...
   
   int AppendLastAccessTimeUtc(byte[] bytes, int offset) {
     Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, bytes, offset, sizeof(long));
     return sizeof(long);
   }
   ```

##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value
+        {
+            get { return _value; }
+            set { _value = value; }
+        }
+        public DateTime LastAccessTimeUtc
+        {
+            get { return _lastAccessTimeUtc; }
+            set { _lastAccessTimeUtc = value; }
+        }
+
+        public DateTime ExpirationTimeUtc
+        {
+            get { return _expirationTimeUtc; }
+            set { _expirationTimeUtc = value; }
+        }
+
+        public TimeSpan SpanUntilStale
+        {
+            get { return _spanUntilStale; }
+            set { _spanUntilStale = value; }
+        }
+
+        public byte[] ToByteArray()
+        {
+            int neededBytes = 3 * sizeof(long) + _value.Length;
+            byte[] byteArray = new byte[neededBytes];
+            int byteIndex = 0;
+
+            // Append LastAccessTimeUtc
+            Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append ExpirationTimeUtc
+            Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append SpanUntilStale
+            Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append the value
+            Array.Copy(_value, 0, byteArray, byteIndex, _value.Length);
+            return byteArray;
+        }
+
+        public void FromByteArray(byte[] data)
+        {
+            int byteIndex = 0;
+
+            // Extract the LastAccessTimeUtc
+            LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the ExpirationTimeUtc
+            ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the SpanUntilStale
+            SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the value
+            Value = new byte[data.Length - byteIndex];
+            Array.Copy(data, byteIndex, _value, 0, data.Length - byteIndex);
+        }
+    }
+
+    public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache
+    {
+        private readonly IGeodeCache _cache;
+        private ILogger<GeodeSessionStateCache> _logger;
+        private static Region _region;
+        private string _regionName;
+        private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+        public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+
+            var host = optionsAccessor.Value.Host;
+            var port = optionsAccessor.Value.Port;
+            _regionName = optionsAccessor.Value.RegionName;
+
+            _cache = CacheFactory.Create()
+                .SetProperty("log-level", "none")
+                .CreateCache();
+
+            _cache.PoolManager
+                .CreatePoolFactory()
+                .AddLocator(host, port)
+                .CreatePool("pool");
+
+            var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+            _region = regionFactory.CreateRegion(_regionName);
+        }
+
+        // Returns the SessionStateValue for key, or null if key doesn't exist
+        public GeodeSessionStateValue GetValueForKey(string key)
+        {
+            byte[] cacheValue = _region.GetByteArray(key);
+
+            if (cacheValue != null)
+            {
+                return new GeodeSessionStateValue(cacheValue);
+            }
+            else
+                return null;
+        }
+
+        public byte[] Get(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Check for nonexistent key
+            GeodeSessionStateValue ssValue = GetValueForKey(key);
+            if (ssValue == null)
+                return null;
+
+            // Check for expired key
+            DateTime nowUtc = DateTime.UtcNow;
+            if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc)
+                return null;
+
+            // Check for stale key
+            if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+              nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale))
+                return null;
+
+            //LogDebug("Inserting against key [" + key + "] with absolute expiration: " +
+            //         options.AbsoluteExpiration.Value.DateTime);
+
+            // Update the times for sliding expirations
+            if (ssValue.SpanUntilStale != TimeSpan.Zero)
+            {
+                ssValue.LastAccessTimeUtc = nowUtc;
+                _region.PutByteArray(key, ssValue.ToByteArray());
+            }
+
+            return ssValue.Value;
+        }
+
+        public Task<byte[]> GetAsync(string key, CancellationToken token = default(CancellationToken))
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            token.ThrowIfCancellationRequested();
+
+            return Task.Factory.StartNew(() => Get(key), token);
+        }
+
+        public void Refresh(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Check for nonexistent key
+            GeodeSessionStateValue ssValue = GetValueForKey(key);
+            if (ssValue == null)
+                return;
+
+            // Check for expired key
+            DateTime nowUtc = DateTime.UtcNow;
+            if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc)
+                return;
+
+            // Check for stale key
+            if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+              nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale))
+                return;
+
+            //LogDebug("Inserting against key [" + key + "] with absolute expiration: " +
+            //         options.AbsoluteExpiration.Value.DateTime);
+
+            // Update the times for sliding expirations
+            if (ssValue.SpanUntilStale != TimeSpan.Zero)
+            {
+                ssValue.LastAccessTimeUtc = nowUtc;
+                _region.PutByteArray(key, ssValue.ToByteArray());
+            }
+        }
+
+        public Task RefreshAsync(string key, CancellationToken token = default(CancellationToken))
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            token.ThrowIfCancellationRequested();
+
+            return Task.Factory.StartNew(() => Refresh(key), token);
+        }
+
+        public void Remove(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Until we return error codes
+            //if (!_cacheRegion.Remove(key))
+            //{
+            //    throw new Exception("Failed to remove from cache");
+            //}
+            _region.Remove(key);
+        }
+
+        public Task RemoveAsync(string key, CancellationToken token = default(CancellationToken))
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            token.ThrowIfCancellationRequested();
+
+            return Task.Factory.StartNew(() => Remove(key), token);
+        }
+
+        public void Set(string key, byte[] value, DistributedCacheEntryOptions options)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            if (value == null)
+            {
+                throw new ArgumentNullException(nameof(value));
+            }
+
+            if (options == null)
+            {
+                throw new ArgumentNullException(nameof(options));
+            }
+
+            Connect();
+
+            GeodeSessionStateValue ssValue = new GeodeSessionStateValue();
+            ssValue.Value = value;
+
+            DateTime nowUtc = DateTime.UtcNow;
+            ssValue.LastAccessTimeUtc = nowUtc;
+
+            // No need to check stale or expired data when setting an absolute expiration.
+            // Think of if as setting a new key/value pair. Expired data will always be cleaned up
+            // when the CleanupExpiredData job runs.
+
+            if (options.AbsoluteExpiration != null)
+            {
+                //LogDebug("Inserting against key [" + key + "] with absolute expiration: " +
+                //         options.AbsoluteExpiration.Value.DateTime);
+                DateTimeOffset dto = options.AbsoluteExpiration.Value;
+                ssValue.ExpirationTimeUtc = dto.DateTime + dto.Offset;
+            }
+
+            // If AbsoluteExpiration and AbsoluteExpirationRelativeToNow are set, use the latter.
+            if (options.AbsoluteExpirationRelativeToNow != null)
+            {
+                //LogDebug("Inserting against key [" + key + "] with absolute expiration: " +
+                //         options.AbsoluteExpiration.Value.DateTime);
+                TimeSpan ts = options.AbsoluteExpirationRelativeToNow.Value;
+                ssValue.ExpirationTimeUtc = nowUtc + ts;
+            }
+
+            if (options.SlidingExpiration != null)
+            {
+                //LogDebug("Inserting against key [" + key + "] with absolute expiration: " +
+                //         options.AbsoluteExpiration.Value.DateTime);
+                ssValue.SpanUntilStale = options.SlidingExpiration.Value;
+            }
+
+            _region.PutByteArray(key, ssValue.ToByteArray());
+            return;
+        }
+
+        public Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default(CancellationToken))
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            token.ThrowIfCancellationRequested();
+
+            return Task.Factory.StartNew(() => Set(key, value, options), token);
+        }
+
+        private void Connect()
+        {
+            if (_region != null)
+            {
+                return;
+            }
+
+            _connectLock.Wait();
+            try
+            {
+                using var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+                try
+                {
+                    _logger?.LogTrace("Create CacheRegion");
+                    _region = regionFactory.CreateRegion(_regionName);
+                    _logger?.LogTrace("CacheRegion created");
+                }
+                catch (Exception e)
+                {
+                    _logger?.LogInformation(e, "Create CacheRegion failed... now trying to get the region");
+                }
+            }
+            finally
+            {
+                //regionFactory?.Dispose();
+                _connectLock.Release();
+            }
+        }
+
+        protected override void DestroyContainedObject()
+        {
+            _region?.Dispose();
+            _region = null;
+        }
+    }
+}

Review comment:
       New line at end of source files.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679283587



##########
File path: netcore/geode-dotnet-core.sln
##########
@@ -7,24 +7,56 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore", "NetCore\NetCore.
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Test", "NetCore.Test\NetCore.Test.csproj", "{501DEA7E-8985-42A8-8BC9-C073E1B6DFE0}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Session", "Session", "{520C96EC-F929-4365-8D78-CC5785419B62}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Session", "NetCore.Session\NetCore.Session.csproj", "{B88C58EB-B144-403B-85F7-7A5B45E643E3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Session.IntegrationTests", "NetCore.Session.IntegrationTests\NetCore.Session.IntegrationTests.csproj", "{94D2CD59-A5F3-4504-BF01-0A3B95CE12B5}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
 		Debug|x64 = Debug|x64
+		Release|Any CPU = Release|Any CPU
 		Release|x64 = Release|x64
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Debug|Any CPU.ActiveCfg = Debug|x64

Review comment:
       Not sure if you can mix. But I agree we should just delete the Any CPU configs. 




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] pivotal-jbarrett commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
pivotal-jbarrett commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r680108376



##########
File path: netcore/NetCore.Session.IntegrationTests/NetCore.Session.IntegrationTests.csproj
##########
@@ -0,0 +1,26 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
+
+    <IsPackable>false</IsPackable>
+
+    <Platforms>x64</Platforms>
+  </PropertyGroup>
+
+  <ItemGroup>
+	<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
+	<PackageReference Include="xunit" Version="2.4.1" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">

Review comment:
       Spacing is not consistent.

##########
File path: netcore/NetCore.Session.IntegrationTests/NetCore.Session.IntegrationTests.csproj
##########
@@ -0,0 +1,26 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
+
+    <IsPackable>false</IsPackable>
+
+    <Platforms>x64</Platforms>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
+    <PackageReference Include="xunit" Version="2.4.1" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
+      <PrivateAssets>all</PrivateAssets>

Review comment:
       You have this one specifically tagged with this `PrivateAssets` tag but none of the others.

##########
File path: netcore/NetCore.Session.IntegrationTests/SessionStateIntegrationTests.cs
##########
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using System;
+using System.Text;
+using Xunit;
+using Apache.Geode.Client;
+using System.Linq;
+using Microsoft.Extensions.Caching.Distributed;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session.IntegrationTests {
+  public class SessionStateIntegrationTests {
+    private static string _regionName = "exampleRegion";
+
+    [Fact]
+    public void SetGet() {
+      var ssCacheOptions = new GeodeSessionStateCacheOptions();
+      ssCacheOptions.Host = "localhost";
+      ssCacheOptions.Port = 10334;
+      ssCacheOptions.RegionName = "exampleRegion";
+
+      using var ssCache = new GeodeSessionStateCache(ssCacheOptions);
+
+      var options = new DistributedCacheEntryOptions();
+      DateTime localTime = DateTime.Now.AddDays(1);
+      DateTimeOffset dateAndOffset =
+          new DateTimeOffset(localTime, TimeZoneInfo.Local.GetUtcOffset(localTime));
+      options.AbsoluteExpiration = dateAndOffset;
+      var testValue = new byte[] { 1, 2, 3, 4, 5 };
+      ssCache.Set("testKey", testValue, options);
+      byte[] value = ssCache.Get("testKey");

Review comment:
       There are numerous places where we are not using `var`.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679219919



##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value
+        {
+            get { return _value; }
+            set { _value = value; }
+        }
+        public DateTime LastAccessTimeUtc
+        {
+            get { return _lastAccessTimeUtc; }
+            set { _lastAccessTimeUtc = value; }
+        }
+
+        public DateTime ExpirationTimeUtc
+        {
+            get { return _expirationTimeUtc; }
+            set { _expirationTimeUtc = value; }
+        }
+
+        public TimeSpan SpanUntilStale
+        {
+            get { return _spanUntilStale; }
+            set { _spanUntilStale = value; }
+        }
+
+        public byte[] ToByteArray()
+        {
+            int neededBytes = 3 * sizeof(long) + _value.Length;
+            byte[] byteArray = new byte[neededBytes];
+            int byteIndex = 0;
+
+            // Append LastAccessTimeUtc
+            Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append ExpirationTimeUtc
+            Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append SpanUntilStale
+            Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append the value
+            Array.Copy(_value, 0, byteArray, byteIndex, _value.Length);
+            return byteArray;
+        }
+
+        public void FromByteArray(byte[] data)
+        {
+            int byteIndex = 0;
+
+            // Extract the LastAccessTimeUtc
+            LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the ExpirationTimeUtc
+            ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the SpanUntilStale
+            SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the value
+            Value = new byte[data.Length - byteIndex];
+            Array.Copy(data, byteIndex, _value, 0, data.Length - byteIndex);
+        }
+    }
+
+    public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache
+    {
+        private readonly IGeodeCache _cache;
+        private ILogger<GeodeSessionStateCache> _logger;
+        private static Region _region;
+        private string _regionName;
+        private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+        public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+
+            var host = optionsAccessor.Value.Host;
+            var port = optionsAccessor.Value.Port;
+            _regionName = optionsAccessor.Value.RegionName;
+
+            _cache = CacheFactory.Create()
+                .SetProperty("log-level", "none")

Review comment:
       Agreed.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679279917



##########
File path: netcore/geode-dotnet-core.sln
##########
@@ -7,24 +7,56 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore", "NetCore\NetCore.
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Test", "NetCore.Test\NetCore.Test.csproj", "{501DEA7E-8985-42A8-8BC9-C073E1B6DFE0}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Session", "Session", "{520C96EC-F929-4365-8D78-CC5785419B62}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Session", "NetCore.Session\NetCore.Session.csproj", "{B88C58EB-B144-403B-85F7-7A5B45E643E3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Session.IntegrationTests", "NetCore.Session.IntegrationTests\NetCore.Session.IntegrationTests.csproj", "{94D2CD59-A5F3-4504-BF01-0A3B95CE12B5}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
 		Debug|x64 = Debug|x64
+		Release|Any CPU = Release|Any CPU
 		Release|x64 = Release|x64
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Debug|Any CPU.ActiveCfg = Debug|x64
 		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Debug|x64.ActiveCfg = Debug|x64
 		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Debug|x64.Build.0 = Debug|x64
+		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Release|Any CPU.ActiveCfg = Release|x64

Review comment:
       Needs to be investigated further.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679496744



##########
File path: netcore/NetCore.Session.IntegrationTests/SessionStateIntegrationTests.cs
##########
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ using System;
+using System.Text;
+using Xunit;
+using Apache.Geode.Client;
+using System.Linq;
+using Microsoft.Extensions.Caching.Distributed;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session.IntegrationTests
+{

Review comment:
       Good catch.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679305759



##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value

Review comment:
       Good catch.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r686440755



##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session {
+  public class GeodeSessionStateValue {
+    public GeodeSessionStateValue() {}
+    public GeodeSessionStateValue(byte[] value) {
+      FromByteArray(value);
+    }
+
+    public byte[] Value { get; set; }
+    public DateTime LastAccessTimeUtc { get; set; }
+    public DateTime ExpirationTimeUtc { get; set; } = DateTime.MinValue;
+    public TimeSpan SpanUntilStale { get; set; } = TimeSpan.Zero;
+
+    public byte[] ToByteArray() {
+      int neededBytes = 3 * sizeof(long) + Value.Length;
+      byte[] byteArray = new byte[neededBytes];
+      int byteIndex = 0;
+
+      Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(Value, 0, byteArray, byteIndex, Value.Length);
+      return byteArray;
+    }
+
+    public void FromByteArray(byte[] data) {
+      int byteIndex = 0;
+
+      LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      Value = new byte[data.Length - byteIndex];
+      Array.Copy(data, byteIndex, Value, 0, data.Length - byteIndex);
+    }
+  }
+
+  public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache {
+    private readonly IGeodeCache _cache;
+    private ILogger<GeodeSessionStateCache> _logger;
+    private static Region _region;
+    private string _logLevel;
+    private string _logFile;
+    private string _regionName;
+    private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+    public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+      var host = optionsAccessor.Value.Host;
+      var port = optionsAccessor.Value.Port;
+      _regionName = optionsAccessor.Value.RegionName;
+      _logLevel = optionsAccessor.Value.LogLevel;
+      _logFile = optionsAccessor.Value.LogFile;
+
+      _cache = CacheFactory.Create()
+                   .SetProperty("log-level", _logLevel)
+                   .SetProperty("log-file", _logFile)
+                   .CreateCache();
+
+      _cache.PoolManager.CreatePoolFactory().AddLocator(host, port).CreatePool("pool");
+
+      var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+      _region = regionFactory.CreateRegion(_regionName);
+    }
+
+    // Returns the SessionStateValue for key, or null if key doesn't exist
+    public GeodeSessionStateValue GetValueForKey(string key) {
+      var cacheValue = _region.GetByteArray(key);
+
+      if (cacheValue != null) {
+        return new GeodeSessionStateValue(cacheValue);
+      } else
+        return null;
+    }
+
+    public byte[] Get(string key) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      Connect();
+
+      // Check for nonexistent key
+      GeodeSessionStateValue ssValue = GetValueForKey(key);
+      if (ssValue == null) {
+        return null;
+      }
+
+      // Check for expired key
+      DateTime nowUtc = DateTime.UtcNow;
+      if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc) {
+        return null;
+      }
+
+      // Check for stale key
+      if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+          nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale)) {
+        return null;
+      }
+
+      // Update the times for sliding expirations
+      if (ssValue.SpanUntilStale != TimeSpan.Zero) {
+        ssValue.LastAccessTimeUtc = nowUtc;
+        _region.PutByteArray(key, ssValue.ToByteArray());
+      }
+
+      return ssValue.Value;
+    }
+
+    public Task<byte[]> GetAsync(string key, CancellationToken token = default(CancellationToken)) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      token.ThrowIfCancellationRequested();
+
+      return Task.Factory.StartNew(() => Get(key), token);
+    }
+
+    public void Refresh(string key) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      Connect();
+
+      // Check for nonexistent key
+      GeodeSessionStateValue ssValue = GetValueForKey(key);
+      if (ssValue == null) {
+        return;
+      }
+
+      // Check for expired key
+      var nowUtc = DateTime.UtcNow;
+      if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc) {
+        return;
+      }
+
+      // Check for stale key
+      if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+          nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale)) {
+        return;
+      }
+
+      // Update the times for sliding expirations
+      if (ssValue.SpanUntilStale != TimeSpan.Zero) {
+        ssValue.LastAccessTimeUtc = nowUtc;
+        _region.PutByteArray(key, ssValue.ToByteArray());
+      }
+    }
+
+    public Task RefreshAsync(string key, CancellationToken token = default(CancellationToken)) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      token.ThrowIfCancellationRequested();
+
+      return Task.Factory.StartNew(() => Refresh(key), token);
+    }
+
+    public void Remove(string key) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      Connect();
+
+      _region.Remove(key);
+    }
+
+    public Task RemoveAsync(string key, CancellationToken token = default(CancellationToken)) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      token.ThrowIfCancellationRequested();
+
+      return Task.Factory.StartNew(() => Remove(key), token);
+    }
+
+    public void Set(string key, byte[] value, DistributedCacheEntryOptions options) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      if (value == null) {
+        throw new ArgumentNullException(nameof(value));
+      }
+
+      if (options == null) {
+        throw new ArgumentNullException(nameof(options));
+      }
+
+      Connect();
+
+      GeodeSessionStateValue ssValue = new GeodeSessionStateValue();

Review comment:
       Good catch. Too many years of no var, no auto.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679498413



##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session {
+  public class GeodeSessionStateValue {
+    //DateTime _lastAccessTimeUtc;
+    //DateTime _expirationTimeUtc = DateTime.MinValue;
+    //TimeSpan _spanUntilStale = TimeSpan.Zero;
+    //private byte[] _value;
+
+    public GeodeSessionStateValue() {}
+    public GeodeSessionStateValue(byte[] value) {
+      FromByteArray(value);
+    }
+
+    public byte[] Value { get; set; }
+    public DateTime LastAccessTimeUtc { get; set; }
+    public DateTime ExpirationTimeUtc { get; set; } = DateTime.MinValue;
+    public TimeSpan SpanUntilStale { get; set; } = TimeSpan.Zero;
+
+    public byte[] ToByteArray() {
+      int neededBytes = 3 * sizeof(long) + Value.Length;
+      byte[] byteArray = new byte[neededBytes];
+      int byteIndex = 0;
+
+      Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(Value, 0, byteArray, byteIndex, Value.Length);
+      return byteArray;
+    }
+
+    public void FromByteArray(byte[] data) {
+      int byteIndex = 0;
+
+      LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      Value = new byte[data.Length - byteIndex];
+      Array.Copy(data, byteIndex, Value, 0, data.Length - byteIndex);
+    }
+  }
+
+  public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache {
+    private readonly IGeodeCache _cache;
+    private ILogger<GeodeSessionStateCache> _logger;
+    private static Region _region;
+    private string _logLevel;
+    private string _logFile;
+    private string _regionName;
+    private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+    public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+      var host = optionsAccessor.Value.Host;
+      var port = optionsAccessor.Value.Port;
+      _regionName = optionsAccessor.Value.RegionName;
+      _logLevel = optionsAccessor.Value.LogLevel;
+      _logFile = optionsAccessor.Value.LogFile;
+
+      _cache = CacheFactory.Create()
+                   .SetProperty("log-level", _logLevel)
+                   .SetProperty("log-file", _logFile)
+                   .CreateCache();
+
+      _cache.PoolManager.CreatePoolFactory().AddLocator(host, port).CreatePool("pool");
+
+      var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+      _region = regionFactory.CreateRegion(_regionName);
+    }
+
+    // Returns the SessionStateValue for key, or null if key doesn't exist
+    public GeodeSessionStateValue GetValueForKey(string key) {
+      byte[] cacheValue = _region.GetByteArray(key);
+
+      if (cacheValue != null) {
+        return new GeodeSessionStateValue(cacheValue);
+      } else
+        return null;
+    }
+
+    public byte[] Get(string key) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      Connect();
+
+      // Check for nonexistent key
+      GeodeSessionStateValue ssValue = GetValueForKey(key);
+      if (ssValue == null) {
+        return null;
+      }
+
+      // Check for expired key
+      DateTime nowUtc = DateTime.UtcNow;
+      if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc) {
+        return null;
+      }
+
+      // Check for stale key
+      if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+          nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale)) {
+        return null;
+      }
+
+      // Update the times for sliding expirations
+      if (ssValue.SpanUntilStale != TimeSpan.Zero) {
+        ssValue.LastAccessTimeUtc = nowUtc;
+        _region.PutByteArray(key, ssValue.ToByteArray());
+      }
+
+      return ssValue.Value;
+    }
+
+    public Task<byte[]> GetAsync(string key, CancellationToken token = default(CancellationToken)) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      token.ThrowIfCancellationRequested();
+
+      return Task.Factory.StartNew(() => Get(key), token);
+    }
+
+    public void Refresh(string key) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      Connect();
+
+      // Check for nonexistent key
+      GeodeSessionStateValue ssValue = GetValueForKey(key);
+      if (ssValue == null) {
+        return;
+      }
+
+      // Check for expired key
+      var nowUtc = DateTime.UtcNow;
+      if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc) {
+        return;
+      }
+
+      // Check for stale key
+      if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+          nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale)) {
+        return;
+      }
+
+      // Update the times for sliding expirations
+      if (ssValue.SpanUntilStale != TimeSpan.Zero) {
+        ssValue.LastAccessTimeUtc = nowUtc;
+        _region.PutByteArray(key, ssValue.ToByteArray());
+      }
+    }
+
+    public Task RefreshAsync(string key, CancellationToken token = default(CancellationToken)) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      token.ThrowIfCancellationRequested();
+
+      return Task.Factory.StartNew(() => Refresh(key), token);
+    }
+
+    public void Remove(string key) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      Connect();
+
+      // Until we return error codes

Review comment:
       Yup.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] pivotal-jbarrett commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
pivotal-jbarrett commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r686205572



##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session {
+  public class GeodeSessionStateValue {
+    public GeodeSessionStateValue() {}
+    public GeodeSessionStateValue(byte[] value) {
+      FromByteArray(value);
+    }
+
+    public byte[] Value { get; set; }
+    public DateTime LastAccessTimeUtc { get; set; }
+    public DateTime ExpirationTimeUtc { get; set; } = DateTime.MinValue;
+    public TimeSpan SpanUntilStale { get; set; } = TimeSpan.Zero;
+
+    public byte[] ToByteArray() {
+      int neededBytes = 3 * sizeof(long) + Value.Length;
+      byte[] byteArray = new byte[neededBytes];
+      int byteIndex = 0;
+
+      Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(Value, 0, byteArray, byteIndex, Value.Length);
+      return byteArray;
+    }
+
+    public void FromByteArray(byte[] data) {
+      int byteIndex = 0;
+
+      LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      Value = new byte[data.Length - byteIndex];
+      Array.Copy(data, byteIndex, Value, 0, data.Length - byteIndex);
+    }
+  }
+
+  public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache {
+    private readonly IGeodeCache _cache;
+    private ILogger<GeodeSessionStateCache> _logger;
+    private static Region _region;
+    private string _logLevel;
+    private string _logFile;
+    private string _regionName;
+    private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+    public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+      var host = optionsAccessor.Value.Host;
+      var port = optionsAccessor.Value.Port;
+      _regionName = optionsAccessor.Value.RegionName;
+      _logLevel = optionsAccessor.Value.LogLevel;
+      _logFile = optionsAccessor.Value.LogFile;
+
+      _cache = CacheFactory.Create()
+                   .SetProperty("log-level", _logLevel)
+                   .SetProperty("log-file", _logFile)
+                   .CreateCache();
+
+      _cache.PoolManager.CreatePoolFactory().AddLocator(host, port).CreatePool("pool");
+
+      var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+      _region = regionFactory.CreateRegion(_regionName);
+    }
+
+    // Returns the SessionStateValue for key, or null if key doesn't exist
+    public GeodeSessionStateValue GetValueForKey(string key) {
+      var cacheValue = _region.GetByteArray(key);
+
+      if (cacheValue != null) {
+        return new GeodeSessionStateValue(cacheValue);
+      } else
+        return null;
+    }
+
+    public byte[] Get(string key) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      Connect();
+
+      // Check for nonexistent key
+      GeodeSessionStateValue ssValue = GetValueForKey(key);
+      if (ssValue == null) {
+        return null;
+      }
+
+      // Check for expired key
+      DateTime nowUtc = DateTime.UtcNow;
+      if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc) {
+        return null;
+      }
+
+      // Check for stale key
+      if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+          nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale)) {
+        return null;
+      }
+
+      // Update the times for sliding expirations
+      if (ssValue.SpanUntilStale != TimeSpan.Zero) {
+        ssValue.LastAccessTimeUtc = nowUtc;
+        _region.PutByteArray(key, ssValue.ToByteArray());
+      }
+
+      return ssValue.Value;
+    }
+
+    public Task<byte[]> GetAsync(string key, CancellationToken token = default(CancellationToken)) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      token.ThrowIfCancellationRequested();
+
+      return Task.Factory.StartNew(() => Get(key), token);
+    }
+
+    public void Refresh(string key) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      Connect();
+
+      // Check for nonexistent key
+      GeodeSessionStateValue ssValue = GetValueForKey(key);
+      if (ssValue == null) {
+        return;
+      }
+
+      // Check for expired key
+      var nowUtc = DateTime.UtcNow;
+      if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc) {
+        return;
+      }
+
+      // Check for stale key
+      if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+          nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale)) {
+        return;
+      }
+
+      // Update the times for sliding expirations
+      if (ssValue.SpanUntilStale != TimeSpan.Zero) {
+        ssValue.LastAccessTimeUtc = nowUtc;
+        _region.PutByteArray(key, ssValue.ToByteArray());
+      }
+    }
+
+    public Task RefreshAsync(string key, CancellationToken token = default(CancellationToken)) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      token.ThrowIfCancellationRequested();
+
+      return Task.Factory.StartNew(() => Refresh(key), token);
+    }
+
+    public void Remove(string key) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      Connect();
+
+      _region.Remove(key);
+    }
+
+    public Task RemoveAsync(string key, CancellationToken token = default(CancellationToken)) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      token.ThrowIfCancellationRequested();
+
+      return Task.Factory.StartNew(() => Remove(key), token);
+    }
+
+    public void Set(string key, byte[] value, DistributedCacheEntryOptions options) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      if (value == null) {
+        throw new ArgumentNullException(nameof(value));
+      }
+
+      if (options == null) {
+        throw new ArgumentNullException(nameof(options));
+      }
+
+      Connect();
+
+      GeodeSessionStateValue ssValue = new GeodeSessionStateValue();

Review comment:
       `var` seems to have been missed in many places in this file.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679308947



##########
File path: netcore/NetCore.Session.IntegrationTests/Properties/launchSettings.json
##########
@@ -0,0 +1,8 @@
+{
+  "profiles": {
+    "Apache.Geode.Session.IntegrationTests": {
+      "commandName": "Project",
+      "nativeDebugging": true
+    }
+  }
+}

Review comment:
       Done




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r680137332



##########
File path: netcore/NetCore.Session.IntegrationTests/NetCore.Session.IntegrationTests.csproj
##########
@@ -0,0 +1,26 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
+
+    <IsPackable>false</IsPackable>
+
+    <Platforms>x64</Platforms>
+  </PropertyGroup>
+
+  <ItemGroup>
+	<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
+	<PackageReference Include="xunit" Version="2.4.1" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">

Review comment:
       Not sure how that private assets got set. Removed.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] pivotal-jbarrett commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
pivotal-jbarrett commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679198864



##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value
+        {
+            get { return _value; }
+            set { _value = value; }
+        }
+        public DateTime LastAccessTimeUtc
+        {
+            get { return _lastAccessTimeUtc; }
+            set { _lastAccessTimeUtc = value; }
+        }
+
+        public DateTime ExpirationTimeUtc
+        {
+            get { return _expirationTimeUtc; }
+            set { _expirationTimeUtc = value; }
+        }
+
+        public TimeSpan SpanUntilStale
+        {
+            get { return _spanUntilStale; }
+            set { _spanUntilStale = value; }
+        }
+
+        public byte[] ToByteArray()
+        {
+            int neededBytes = 3 * sizeof(long) + _value.Length;
+            byte[] byteArray = new byte[neededBytes];
+            int byteIndex = 0;
+
+            // Append LastAccessTimeUtc
+            Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append ExpirationTimeUtc
+            Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append SpanUntilStale
+            Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append the value
+            Array.Copy(_value, 0, byteArray, byteIndex, _value.Length);
+            return byteArray;
+        }
+
+        public void FromByteArray(byte[] data)
+        {
+            int byteIndex = 0;
+
+            // Extract the LastAccessTimeUtc
+            LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the ExpirationTimeUtc
+            ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the SpanUntilStale
+            SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the value
+            Value = new byte[data.Length - byteIndex];
+            Array.Copy(data, byteIndex, _value, 0, data.Length - byteIndex);
+        }
+    }
+
+    public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache
+    {
+        private readonly IGeodeCache _cache;
+        private ILogger<GeodeSessionStateCache> _logger;
+        private static Region _region;
+        private string _regionName;
+        private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+        public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+
+            var host = optionsAccessor.Value.Host;
+            var port = optionsAccessor.Value.Port;
+            _regionName = optionsAccessor.Value.RegionName;
+
+            _cache = CacheFactory.Create()
+                .SetProperty("log-level", "none")
+                .CreateCache();
+
+            _cache.PoolManager
+                .CreatePoolFactory()
+                .AddLocator(host, port)
+                .CreatePool("pool");
+
+            var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+            _region = regionFactory.CreateRegion(_regionName);
+        }
+
+        // Returns the SessionStateValue for key, or null if key doesn't exist
+        public GeodeSessionStateValue GetValueForKey(string key)
+        {
+            byte[] cacheValue = _region.GetByteArray(key);
+
+            if (cacheValue != null)
+            {
+                return new GeodeSessionStateValue(cacheValue);
+            }
+            else
+                return null;
+        }
+
+        public byte[] Get(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Check for nonexistent key
+            GeodeSessionStateValue ssValue = GetValueForKey(key);
+            if (ssValue == null)

Review comment:
       It doesn't look like clang-format was ever applied.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679300684



##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value
+        {
+            get { return _value; }
+            set { _value = value; }
+        }
+        public DateTime LastAccessTimeUtc
+        {
+            get { return _lastAccessTimeUtc; }
+            set { _lastAccessTimeUtc = value; }
+        }
+
+        public DateTime ExpirationTimeUtc
+        {
+            get { return _expirationTimeUtc; }
+            set { _expirationTimeUtc = value; }
+        }
+
+        public TimeSpan SpanUntilStale
+        {
+            get { return _spanUntilStale; }
+            set { _spanUntilStale = value; }
+        }
+
+        public byte[] ToByteArray()
+        {
+            int neededBytes = 3 * sizeof(long) + _value.Length;
+            byte[] byteArray = new byte[neededBytes];
+            int byteIndex = 0;
+
+            // Append LastAccessTimeUtc
+            Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append ExpirationTimeUtc
+            Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append SpanUntilStale
+            Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append the value
+            Array.Copy(_value, 0, byteArray, byteIndex, _value.Length);
+            return byteArray;
+        }
+
+        public void FromByteArray(byte[] data)
+        {
+            int byteIndex = 0;
+
+            // Extract the LastAccessTimeUtc
+            LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the ExpirationTimeUtc
+            ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the SpanUntilStale
+            SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the value
+            Value = new byte[data.Length - byteIndex];
+            Array.Copy(data, byteIndex, _value, 0, data.Length - byteIndex);
+        }
+    }
+
+    public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache
+    {
+        private readonly IGeodeCache _cache;
+        private ILogger<GeodeSessionStateCache> _logger;
+        private static Region _region;
+        private string _regionName;
+        private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+        public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+
+            var host = optionsAccessor.Value.Host;
+            var port = optionsAccessor.Value.Port;
+            _regionName = optionsAccessor.Value.RegionName;
+
+            _cache = CacheFactory.Create()
+                .SetProperty("log-level", "none")
+                .CreateCache();
+
+            _cache.PoolManager
+                .CreatePoolFactory()
+                .AddLocator(host, port)
+                .CreatePool("pool");
+
+            var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+            _region = regionFactory.CreateRegion(_regionName);
+        }
+
+        // Returns the SessionStateValue for key, or null if key doesn't exist
+        public GeodeSessionStateValue GetValueForKey(string key)
+        {
+            byte[] cacheValue = _region.GetByteArray(key);
+
+            if (cacheValue != null)
+            {
+                return new GeodeSessionStateValue(cacheValue);
+            }
+            else
+                return null;
+        }
+
+        public byte[] Get(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Check for nonexistent key
+            GeodeSessionStateValue ssValue = GetValueForKey(key);
+            if (ssValue == null)
+                return null;
+
+            // Check for expired key
+            DateTime nowUtc = DateTime.UtcNow;
+            if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc)
+                return null;
+
+            // Check for stale key
+            if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+              nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale))
+                return null;
+
+            //LogDebug("Inserting against key [" + key + "] with absolute expiration: " +
+            //         options.AbsoluteExpiration.Value.DateTime);
+
+            // Update the times for sliding expirations
+            if (ssValue.SpanUntilStale != TimeSpan.Zero)
+            {
+                ssValue.LastAccessTimeUtc = nowUtc;
+                _region.PutByteArray(key, ssValue.ToByteArray());
+            }
+
+            return ssValue.Value;
+        }
+
+        public Task<byte[]> GetAsync(string key, CancellationToken token = default(CancellationToken))
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            token.ThrowIfCancellationRequested();
+
+            return Task.Factory.StartNew(() => Get(key), token);
+        }
+
+        public void Refresh(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Check for nonexistent key
+            GeodeSessionStateValue ssValue = GetValueForKey(key);
+            if (ssValue == null)
+                return;
+
+            // Check for expired key
+            DateTime nowUtc = DateTime.UtcNow;
+            if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc)
+                return;
+
+            // Check for stale key
+            if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+              nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale))
+                return;
+
+            //LogDebug("Inserting against key [" + key + "] with absolute expiration: " +
+            //         options.AbsoluteExpiration.Value.DateTime);
+
+            // Update the times for sliding expirations
+            if (ssValue.SpanUntilStale != TimeSpan.Zero)
+            {
+                ssValue.LastAccessTimeUtc = nowUtc;
+                _region.PutByteArray(key, ssValue.ToByteArray());
+            }
+        }
+
+        public Task RefreshAsync(string key, CancellationToken token = default(CancellationToken))
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            token.ThrowIfCancellationRequested();
+
+            return Task.Factory.StartNew(() => Refresh(key), token);
+        }
+
+        public void Remove(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Until we return error codes
+            //if (!_cacheRegion.Remove(key))
+            //{
+            //    throw new Exception("Failed to remove from cache");
+            //}
+            _region.Remove(key);
+        }
+
+        public Task RemoveAsync(string key, CancellationToken token = default(CancellationToken))
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            token.ThrowIfCancellationRequested();
+
+            return Task.Factory.StartNew(() => Remove(key), token);
+        }
+
+        public void Set(string key, byte[] value, DistributedCacheEntryOptions options)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            if (value == null)
+            {
+                throw new ArgumentNullException(nameof(value));
+            }
+
+            if (options == null)
+            {
+                throw new ArgumentNullException(nameof(options));
+            }
+
+            Connect();
+
+            GeodeSessionStateValue ssValue = new GeodeSessionStateValue();
+            ssValue.Value = value;
+
+            DateTime nowUtc = DateTime.UtcNow;
+            ssValue.LastAccessTimeUtc = nowUtc;
+
+            // No need to check stale or expired data when setting an absolute expiration.
+            // Think of if as setting a new key/value pair. Expired data will always be cleaned up
+            // when the CleanupExpiredData job runs.
+
+            if (options.AbsoluteExpiration != null)
+            {
+                //LogDebug("Inserting against key [" + key + "] with absolute expiration: " +
+                //         options.AbsoluteExpiration.Value.DateTime);
+                DateTimeOffset dto = options.AbsoluteExpiration.Value;
+                ssValue.ExpirationTimeUtc = dto.DateTime + dto.Offset;
+            }
+
+            // If AbsoluteExpiration and AbsoluteExpirationRelativeToNow are set, use the latter.
+            if (options.AbsoluteExpirationRelativeToNow != null)
+            {
+                //LogDebug("Inserting against key [" + key + "] with absolute expiration: " +
+                //         options.AbsoluteExpiration.Value.DateTime);
+                TimeSpan ts = options.AbsoluteExpirationRelativeToNow.Value;
+                ssValue.ExpirationTimeUtc = nowUtc + ts;
+            }
+
+            if (options.SlidingExpiration != null)
+            {
+                //LogDebug("Inserting against key [" + key + "] with absolute expiration: " +
+                //         options.AbsoluteExpiration.Value.DateTime);
+                ssValue.SpanUntilStale = options.SlidingExpiration.Value;
+            }
+
+            _region.PutByteArray(key, ssValue.ToByteArray());
+            return;
+        }
+
+        public Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default(CancellationToken))
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            token.ThrowIfCancellationRequested();
+
+            return Task.Factory.StartNew(() => Set(key, value, options), token);
+        }
+
+        private void Connect()
+        {
+            if (_region != null)
+            {
+                return;
+            }
+
+            _connectLock.Wait();
+            try
+            {
+                using var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+                try
+                {
+                    _logger?.LogTrace("Create CacheRegion");
+                    _region = regionFactory.CreateRegion(_regionName);
+                    _logger?.LogTrace("CacheRegion created");
+                }
+                catch (Exception e)
+                {
+                    _logger?.LogInformation(e, "Create CacheRegion failed... now trying to get the region");
+                }
+            }
+            finally
+            {
+                //regionFactory?.Dispose();
+                _connectLock.Release();
+            }
+        }
+
+        protected override void DestroyContainedObject()
+        {
+            _region?.Dispose();
+            _region = null;
+        }
+    }
+}

Review comment:
       Done




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679497999



##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session {
+  public class GeodeSessionStateValue {
+    //DateTime _lastAccessTimeUtc;
+    //DateTime _expirationTimeUtc = DateTime.MinValue;
+    //TimeSpan _spanUntilStale = TimeSpan.Zero;
+    //private byte[] _value;
+
+    public GeodeSessionStateValue() {}
+    public GeodeSessionStateValue(byte[] value) {
+      FromByteArray(value);
+    }
+
+    public byte[] Value { get; set; }
+    public DateTime LastAccessTimeUtc { get; set; }
+    public DateTime ExpirationTimeUtc { get; set; } = DateTime.MinValue;
+    public TimeSpan SpanUntilStale { get; set; } = TimeSpan.Zero;
+
+    public byte[] ToByteArray() {
+      int neededBytes = 3 * sizeof(long) + Value.Length;
+      byte[] byteArray = new byte[neededBytes];
+      int byteIndex = 0;
+
+      Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(Value, 0, byteArray, byteIndex, Value.Length);
+      return byteArray;
+    }
+
+    public void FromByteArray(byte[] data) {
+      int byteIndex = 0;
+
+      LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      Value = new byte[data.Length - byteIndex];
+      Array.Copy(data, byteIndex, Value, 0, data.Length - byteIndex);
+    }
+  }
+
+  public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache {
+    private readonly IGeodeCache _cache;
+    private ILogger<GeodeSessionStateCache> _logger;
+    private static Region _region;
+    private string _logLevel;
+    private string _logFile;
+    private string _regionName;
+    private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+    public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+      var host = optionsAccessor.Value.Host;
+      var port = optionsAccessor.Value.Port;
+      _regionName = optionsAccessor.Value.RegionName;
+      _logLevel = optionsAccessor.Value.LogLevel;
+      _logFile = optionsAccessor.Value.LogFile;
+
+      _cache = CacheFactory.Create()
+                   .SetProperty("log-level", _logLevel)
+                   .SetProperty("log-file", _logFile)
+                   .CreateCache();
+
+      _cache.PoolManager.CreatePoolFactory().AddLocator(host, port).CreatePool("pool");
+
+      var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+      _region = regionFactory.CreateRegion(_regionName);
+    }
+
+    // Returns the SessionStateValue for key, or null if key doesn't exist
+    public GeodeSessionStateValue GetValueForKey(string key) {
+      byte[] cacheValue = _region.GetByteArray(key);

Review comment:
       Good catch.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] pdxcodemonkey merged pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
pdxcodemonkey merged pull request #834:
URL: https://github.com/apache/geode-native/pull/834


   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679226501



##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value
+        {
+            get { return _value; }
+            set { _value = value; }
+        }
+        public DateTime LastAccessTimeUtc
+        {
+            get { return _lastAccessTimeUtc; }
+            set { _lastAccessTimeUtc = value; }
+        }
+
+        public DateTime ExpirationTimeUtc
+        {
+            get { return _expirationTimeUtc; }
+            set { _expirationTimeUtc = value; }
+        }
+
+        public TimeSpan SpanUntilStale
+        {
+            get { return _spanUntilStale; }
+            set { _spanUntilStale = value; }
+        }
+
+        public byte[] ToByteArray()
+        {
+            int neededBytes = 3 * sizeof(long) + _value.Length;
+            byte[] byteArray = new byte[neededBytes];
+            int byteIndex = 0;
+
+            // Append LastAccessTimeUtc
+            Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append ExpirationTimeUtc
+            Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append SpanUntilStale
+            Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append the value
+            Array.Copy(_value, 0, byteArray, byteIndex, _value.Length);
+            return byteArray;
+        }
+
+        public void FromByteArray(byte[] data)
+        {
+            int byteIndex = 0;
+
+            // Extract the LastAccessTimeUtc
+            LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the ExpirationTimeUtc
+            ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the SpanUntilStale
+            SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the value
+            Value = new byte[data.Length - byteIndex];
+            Array.Copy(data, byteIndex, _value, 0, data.Length - byteIndex);
+        }
+    }
+
+    public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache
+    {
+        private readonly IGeodeCache _cache;
+        private ILogger<GeodeSessionStateCache> _logger;
+        private static Region _region;
+        private string _regionName;
+        private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+        public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+
+            var host = optionsAccessor.Value.Host;
+            var port = optionsAccessor.Value.Port;
+            _regionName = optionsAccessor.Value.RegionName;
+
+            _cache = CacheFactory.Create()
+                .SetProperty("log-level", "none")
+                .CreateCache();
+
+            _cache.PoolManager
+                .CreatePoolFactory()
+                .AddLocator(host, port)
+                .CreatePool("pool");
+
+            var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+            _region = regionFactory.CreateRegion(_regionName);
+        }
+
+        // Returns the SessionStateValue for key, or null if key doesn't exist
+        public GeodeSessionStateValue GetValueForKey(string key)
+        {
+            byte[] cacheValue = _region.GetByteArray(key);
+
+            if (cacheValue != null)
+            {
+                return new GeodeSessionStateValue(cacheValue);
+            }
+            else
+                return null;
+        }
+
+        public byte[] Get(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Check for nonexistent key
+            GeodeSessionStateValue ssValue = GetValueForKey(key);
+            if (ssValue == null)
+                return null;
+
+            // Check for expired key
+            DateTime nowUtc = DateTime.UtcNow;

Review comment:
       Good catch.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r680142566



##########
File path: netcore/NetCore.Session.IntegrationTests/SessionStateIntegrationTests.cs
##########
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using System;
+using System.Text;
+using Xunit;
+using Apache.Geode.Client;
+using System.Linq;
+using Microsoft.Extensions.Caching.Distributed;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session.IntegrationTests {
+  public class SessionStateIntegrationTests {
+    private static string _regionName = "exampleRegion";
+
+    [Fact]
+    public void SetGet() {
+      var ssCacheOptions = new GeodeSessionStateCacheOptions();
+      ssCacheOptions.Host = "localhost";
+      ssCacheOptions.Port = 10334;
+      ssCacheOptions.RegionName = "exampleRegion";
+
+      using var ssCache = new GeodeSessionStateCache(ssCacheOptions);
+
+      var options = new DistributedCacheEntryOptions();
+      DateTime localTime = DateTime.Now.AddDays(1);
+      DateTimeOffset dateAndOffset =
+          new DateTimeOffset(localTime, TimeZoneInfo.Local.GetUtcOffset(localTime));
+      options.AbsoluteExpiration = dateAndOffset;
+      var testValue = new byte[] { 1, 2, 3, 4, 5 };
+      ssCache.Set("testKey", testValue, options);
+      byte[] value = ssCache.Get("testKey");

Review comment:
       Good catch. 




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679486286



##########
File path: netcore/NetCore.Session.IntegrationTests/NetCore.Session.IntegrationTests.csproj
##########
@@ -0,0 +1,26 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
+
+    <IsPackable>false</IsPackable>
+
+    <Platforms>x64</Platforms>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />

Review comment:
       Looks like this is only needed for running tests in Visual Studio. If you run tests from the command line like the CI will do (i.e., dotnet test) this dependency isn't needed. I'm OK with removing it.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] pdxcodemonkey commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
pdxcodemonkey commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r678601117



##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value
+        {
+            get { return _value; }
+            set { _value = value; }
+        }
+        public DateTime LastAccessTimeUtc
+        {
+            get { return _lastAccessTimeUtc; }
+            set { _lastAccessTimeUtc = value; }
+        }
+
+        public DateTime ExpirationTimeUtc
+        {
+            get { return _expirationTimeUtc; }
+            set { _expirationTimeUtc = value; }
+        }
+
+        public TimeSpan SpanUntilStale
+        {
+            get { return _spanUntilStale; }
+            set { _spanUntilStale = value; }
+        }
+
+        public byte[] ToByteArray()
+        {
+            int neededBytes = 3 * sizeof(long) + _value.Length;
+            byte[] byteArray = new byte[neededBytes];
+            int byteIndex = 0;
+
+            // Append LastAccessTimeUtc
+            Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append ExpirationTimeUtc
+            Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append SpanUntilStale
+            Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append the value
+            Array.Copy(_value, 0, byteArray, byteIndex, _value.Length);
+            return byteArray;
+        }
+
+        public void FromByteArray(byte[] data)
+        {
+            int byteIndex = 0;
+
+            // Extract the LastAccessTimeUtc
+            LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the ExpirationTimeUtc
+            ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the SpanUntilStale
+            SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the value
+            Value = new byte[data.Length - byteIndex];
+            Array.Copy(data, byteIndex, _value, 0, data.Length - byteIndex);
+        }
+    }
+
+    public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache
+    {
+        private readonly IGeodeCache _cache;
+        private ILogger<GeodeSessionStateCache> _logger;
+        private static Region _region;
+        private string _regionName;
+        private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+        public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+
+            var host = optionsAccessor.Value.Host;
+            var port = optionsAccessor.Value.Port;
+            _regionName = optionsAccessor.Value.RegionName;
+
+            _cache = CacheFactory.Create()
+                .SetProperty("log-level", "none")
+                .CreateCache();
+
+            _cache.PoolManager
+                .CreatePoolFactory()
+                .AddLocator(host, port)
+                .CreatePool("pool");
+
+            var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+            _region = regionFactory.CreateRegion(_regionName);
+        }
+
+        // Returns the SessionStateValue for key, or null if key doesn't exist
+        public GeodeSessionStateValue GetValueForKey(string key)
+        {
+            byte[] cacheValue = _region.GetByteArray(key);
+
+            if (cacheValue != null)
+            {
+                return new GeodeSessionStateValue(cacheValue);
+            }
+            else
+                return null;
+        }
+
+        public byte[] Get(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Check for nonexistent key
+            GeodeSessionStateValue ssValue = GetValueForKey(key);
+            if (ssValue == null)
+                return null;
+
+            // Check for expired key
+            DateTime nowUtc = DateTime.UtcNow;
+            if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc)
+                return null;
+
+            // Check for stale key
+            if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+              nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale))
+                return null;
+
+            //LogDebug("Inserting against key [" + key + "] with absolute expiration: " +
+            //         options.AbsoluteExpiration.Value.DateTime);
+
+            // Update the times for sliding expirations
+            if (ssValue.SpanUntilStale != TimeSpan.Zero)
+            {
+                ssValue.LastAccessTimeUtc = nowUtc;
+                _region.PutByteArray(key, ssValue.ToByteArray());
+            }
+
+            return ssValue.Value;
+        }
+
+        public Task<byte[]> GetAsync(string key, CancellationToken token = default(CancellationToken))
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            token.ThrowIfCancellationRequested();
+
+            return Task.Factory.StartNew(() => Get(key), token);
+        }
+
+        public void Refresh(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Check for nonexistent key
+            GeodeSessionStateValue ssValue = GetValueForKey(key);
+            if (ssValue == null)

Review comment:
       More single-line if statements without braces.  Just, no.  Pls add braces.

##########
File path: netcore/NetCore.Session.IntegrationTests/Properties/launchSettings.json
##########
@@ -0,0 +1,8 @@
+{
+  "profiles": {
+    "Apache.Geode.Session.IntegrationTests": {
+      "commandName": "Project",
+      "nativeDebugging": true
+    }
+  }
+}

Review comment:
       Missing newline at EOF.  Where there's one of these, there will probably be others.
   

##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value
+        {
+            get { return _value; }
+            set { _value = value; }
+        }
+        public DateTime LastAccessTimeUtc
+        {
+            get { return _lastAccessTimeUtc; }
+            set { _lastAccessTimeUtc = value; }
+        }
+
+        public DateTime ExpirationTimeUtc
+        {
+            get { return _expirationTimeUtc; }
+            set { _expirationTimeUtc = value; }
+        }
+
+        public TimeSpan SpanUntilStale
+        {
+            get { return _spanUntilStale; }
+            set { _spanUntilStale = value; }
+        }
+
+        public byte[] ToByteArray()
+        {
+            int neededBytes = 3 * sizeof(long) + _value.Length;
+            byte[] byteArray = new byte[neededBytes];
+            int byteIndex = 0;
+
+            // Append LastAccessTimeUtc
+            Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append ExpirationTimeUtc
+            Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append SpanUntilStale
+            Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append the value
+            Array.Copy(_value, 0, byteArray, byteIndex, _value.Length);
+            return byteArray;
+        }
+
+        public void FromByteArray(byte[] data)
+        {
+            int byteIndex = 0;
+
+            // Extract the LastAccessTimeUtc
+            LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the ExpirationTimeUtc
+            ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the SpanUntilStale
+            SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the value
+            Value = new byte[data.Length - byteIndex];
+            Array.Copy(data, byteIndex, _value, 0, data.Length - byteIndex);
+        }
+    }
+
+    public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache
+    {
+        private readonly IGeodeCache _cache;
+        private ILogger<GeodeSessionStateCache> _logger;
+        private static Region _region;
+        private string _regionName;
+        private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+        public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+
+            var host = optionsAccessor.Value.Host;
+            var port = optionsAccessor.Value.Port;
+            _regionName = optionsAccessor.Value.RegionName;
+
+            _cache = CacheFactory.Create()
+                .SetProperty("log-level", "none")
+                .CreateCache();
+
+            _cache.PoolManager
+                .CreatePoolFactory()
+                .AddLocator(host, port)
+                .CreatePool("pool");
+
+            var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+            _region = regionFactory.CreateRegion(_regionName);
+        }
+
+        // Returns the SessionStateValue for key, or null if key doesn't exist
+        public GeodeSessionStateValue GetValueForKey(string key)
+        {
+            byte[] cacheValue = _region.GetByteArray(key);
+
+            if (cacheValue != null)
+            {
+                return new GeodeSessionStateValue(cacheValue);
+            }
+            else
+                return null;
+        }
+
+        public byte[] Get(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Check for nonexistent key
+            GeodeSessionStateValue ssValue = GetValueForKey(key);
+            if (ssValue == null)
+                return null;
+
+            // Check for expired key
+            DateTime nowUtc = DateTime.UtcNow;
+            if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc)
+                return null;
+
+            // Check for stale key
+            if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+              nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale))
+                return null;
+
+            //LogDebug("Inserting against key [" + key + "] with absolute expiration: " +

Review comment:
       Remove commented code, pls.

##########
File path: netcore/geode-dotnet-core.sln
##########
@@ -7,24 +7,56 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore", "NetCore\NetCore.
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Test", "NetCore.Test\NetCore.Test.csproj", "{501DEA7E-8985-42A8-8BC9-C073E1B6DFE0}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Session", "Session", "{520C96EC-F929-4365-8D78-CC5785419B62}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Session", "NetCore.Session\NetCore.Session.csproj", "{B88C58EB-B144-403B-85F7-7A5B45E643E3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Session.IntegrationTests", "NetCore.Session.IntegrationTests\NetCore.Session.IntegrationTests.csproj", "{94D2CD59-A5F3-4504-BF01-0A3B95CE12B5}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
 		Debug|x64 = Debug|x64
+		Release|Any CPU = Release|Any CPU
 		Release|x64 = Release|x64
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Debug|Any CPU.ActiveCfg = Debug|x64
 		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Debug|x64.ActiveCfg = Debug|x64
 		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Debug|x64.Build.0 = Debug|x64
+		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Release|Any CPU.ActiveCfg = Release|x64

Review comment:
       Figuring out how to build that flavor using `dotnet build` is still a to-do item.  I tried just changing the name, and it still didn't build symbols, so there's some kind of secret sauce I have to go figure out.

##########
File path: netcore/geode-dotnet-core.sln
##########
@@ -7,24 +7,56 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore", "NetCore\NetCore.
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Test", "NetCore.Test\NetCore.Test.csproj", "{501DEA7E-8985-42A8-8BC9-C073E1B6DFE0}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Session", "Session", "{520C96EC-F929-4365-8D78-CC5785419B62}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Session", "NetCore.Session\NetCore.Session.csproj", "{B88C58EB-B144-403B-85F7-7A5B45E643E3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore.Session.IntegrationTests", "NetCore.Session.IntegrationTests\NetCore.Session.IntegrationTests.csproj", "{94D2CD59-A5F3-4504-BF01-0A3B95CE12B5}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
 		Debug|x64 = Debug|x64
+		Release|Any CPU = Release|Any CPU
 		Release|x64 = Release|x64
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{09ABBCE7-B217-43F1-A51B-CC5BDCD8EE98}.Debug|Any CPU.ActiveCfg = Debug|x64

Review comment:
       I don't think we need `Any CPU` configs for any of these projects, let's just get rid of 'em.  Almost certainly you can't build `Apache.Geode.Client` for Any CPU, and it doesn't appear the .sln format lets you keep it on a per-project bases.

##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value

Review comment:
       These are all trivial getters/setters, so all these properties should be auto-implemented a la RegionName, Host, and Port in the options class.

##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value
+        {
+            get { return _value; }
+            set { _value = value; }
+        }
+        public DateTime LastAccessTimeUtc
+        {
+            get { return _lastAccessTimeUtc; }
+            set { _lastAccessTimeUtc = value; }
+        }
+
+        public DateTime ExpirationTimeUtc
+        {
+            get { return _expirationTimeUtc; }
+            set { _expirationTimeUtc = value; }
+        }
+
+        public TimeSpan SpanUntilStale
+        {
+            get { return _spanUntilStale; }
+            set { _spanUntilStale = value; }
+        }
+
+        public byte[] ToByteArray()
+        {
+            int neededBytes = 3 * sizeof(long) + _value.Length;
+            byte[] byteArray = new byte[neededBytes];
+            int byteIndex = 0;
+
+            // Append LastAccessTimeUtc
+            Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append ExpirationTimeUtc
+            Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append SpanUntilStale
+            Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append the value
+            Array.Copy(_value, 0, byteArray, byteIndex, _value.Length);
+            return byteArray;
+        }
+
+        public void FromByteArray(byte[] data)
+        {
+            int byteIndex = 0;
+
+            // Extract the LastAccessTimeUtc
+            LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the ExpirationTimeUtc
+            ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the SpanUntilStale
+            SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the value
+            Value = new byte[data.Length - byteIndex];
+            Array.Copy(data, byteIndex, _value, 0, data.Length - byteIndex);
+        }
+    }
+
+    public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache
+    {
+        private readonly IGeodeCache _cache;
+        private ILogger<GeodeSessionStateCache> _logger;
+        private static Region _region;
+        private string _regionName;
+        private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+        public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+
+            var host = optionsAccessor.Value.Host;
+            var port = optionsAccessor.Value.Port;
+            _regionName = optionsAccessor.Value.RegionName;
+
+            _cache = CacheFactory.Create()
+                .SetProperty("log-level", "none")

Review comment:
       This is a _great_ suggestion - let's externalize log-level for the session, it'll pay us back later.

##########
File path: netcore/NetCore.Session/GeodeCacheServiceCollectionExtensions.cs
##########
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ using System;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Apache.Geode.Session
+{
+    public static class GeodeCacheServiceCollectionExtensions
+    {

Review comment:
       Hint - you should be able to run `clang-format` locally on these, using the .clang-format file in `/netcore`.

##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session
+{
+    public class GeodeSessionStateValue
+    {
+        DateTime _lastAccessTimeUtc;
+        DateTime _expirationTimeUtc = DateTime.MinValue;
+        TimeSpan _spanUntilStale = TimeSpan.Zero;
+        private byte[] _value;
+
+        public GeodeSessionStateValue() { }
+        public GeodeSessionStateValue(byte[] value)
+        {
+            FromByteArray(value);
+        }
+
+        public byte[] Value
+        {
+            get { return _value; }
+            set { _value = value; }
+        }
+        public DateTime LastAccessTimeUtc
+        {
+            get { return _lastAccessTimeUtc; }
+            set { _lastAccessTimeUtc = value; }
+        }
+
+        public DateTime ExpirationTimeUtc
+        {
+            get { return _expirationTimeUtc; }
+            set { _expirationTimeUtc = value; }
+        }
+
+        public TimeSpan SpanUntilStale
+        {
+            get { return _spanUntilStale; }
+            set { _spanUntilStale = value; }
+        }
+
+        public byte[] ToByteArray()
+        {
+            int neededBytes = 3 * sizeof(long) + _value.Length;
+            byte[] byteArray = new byte[neededBytes];
+            int byteIndex = 0;
+
+            // Append LastAccessTimeUtc
+            Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append ExpirationTimeUtc
+            Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append SpanUntilStale
+            Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex, sizeof(long));
+            byteIndex += sizeof(long);
+
+            // Append the value
+            Array.Copy(_value, 0, byteArray, byteIndex, _value.Length);
+            return byteArray;
+        }
+
+        public void FromByteArray(byte[] data)
+        {
+            int byteIndex = 0;
+
+            // Extract the LastAccessTimeUtc
+            LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the ExpirationTimeUtc
+            ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the SpanUntilStale
+            SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+            byteIndex += sizeof(long);
+
+            // Extract the value
+            Value = new byte[data.Length - byteIndex];
+            Array.Copy(data, byteIndex, _value, 0, data.Length - byteIndex);
+        }
+    }
+
+    public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache
+    {
+        private readonly IGeodeCache _cache;
+        private ILogger<GeodeSessionStateCache> _logger;
+        private static Region _region;
+        private string _regionName;
+        private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+        public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+
+            var host = optionsAccessor.Value.Host;
+            var port = optionsAccessor.Value.Port;
+            _regionName = optionsAccessor.Value.RegionName;
+
+            _cache = CacheFactory.Create()
+                .SetProperty("log-level", "none")
+                .CreateCache();
+
+            _cache.PoolManager
+                .CreatePoolFactory()
+                .AddLocator(host, port)
+                .CreatePool("pool");
+
+            var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+            _region = regionFactory.CreateRegion(_regionName);
+        }
+
+        // Returns the SessionStateValue for key, or null if key doesn't exist
+        public GeodeSessionStateValue GetValueForKey(string key)
+        {
+            byte[] cacheValue = _region.GetByteArray(key);
+
+            if (cacheValue != null)
+            {
+                return new GeodeSessionStateValue(cacheValue);
+            }
+            else
+                return null;
+        }
+
+        public byte[] Get(string key)
+        {
+            if (key == null)
+            {
+                throw new ArgumentNullException(nameof(key));
+            }
+
+            Connect();
+
+            // Check for nonexistent key
+            GeodeSessionStateValue ssValue = GetValueForKey(key);
+            if (ssValue == null)

Review comment:
       OMG can't believe I missed this(!).  Shouldn't `clang-format` or LGTM or something enforce this for us?  Guess we're gonna struggle a little bit until we get the analysis & formatting tools incorporated into CI builds of netcore.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] mmartell commented on pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
mmartell commented on pull request #834:
URL: https://github.com/apache/geode-native/pull/834#issuecomment-890080462


   Last commit is to remove the examples/dotnetcore directory, which was added by accident.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [geode-native] pivotal-jbarrett commented on a change in pull request #834: GEODE-9359: Add NetCore SessionState support

Posted by GitBox <gi...@apache.org>.
pivotal-jbarrett commented on a change in pull request #834:
URL: https://github.com/apache/geode-native/pull/834#discussion_r679359604



##########
File path: netcore/NetCore.Session.IntegrationTests/SessionStateIntegrationTests.cs
##########
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ using System;
+using System.Text;
+using Xunit;
+using Apache.Geode.Client;
+using System.Linq;
+using Microsoft.Extensions.Caching.Distributed;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session.IntegrationTests
+{

Review comment:
       Doesn't match the other formatting. 

##########
File path: netcore/NetCore.Session.IntegrationTests/NetCore.Session.IntegrationTests.csproj
##########
@@ -0,0 +1,26 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
+
+    <IsPackable>false</IsPackable>
+
+    <Platforms>x64</Platforms>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />

Review comment:
       Do we really have a direct dependency on this?

##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session {
+  public class GeodeSessionStateValue {
+    //DateTime _lastAccessTimeUtc;
+    //DateTime _expirationTimeUtc = DateTime.MinValue;
+    //TimeSpan _spanUntilStale = TimeSpan.Zero;
+    //private byte[] _value;
+
+    public GeodeSessionStateValue() {}
+    public GeodeSessionStateValue(byte[] value) {
+      FromByteArray(value);
+    }
+
+    public byte[] Value { get; set; }
+    public DateTime LastAccessTimeUtc { get; set; }
+    public DateTime ExpirationTimeUtc { get; set; } = DateTime.MinValue;
+    public TimeSpan SpanUntilStale { get; set; } = TimeSpan.Zero;
+
+    public byte[] ToByteArray() {
+      int neededBytes = 3 * sizeof(long) + Value.Length;
+      byte[] byteArray = new byte[neededBytes];
+      int byteIndex = 0;
+
+      Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(Value, 0, byteArray, byteIndex, Value.Length);
+      return byteArray;
+    }
+
+    public void FromByteArray(byte[] data) {
+      int byteIndex = 0;
+
+      LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      Value = new byte[data.Length - byteIndex];
+      Array.Copy(data, byteIndex, Value, 0, data.Length - byteIndex);
+    }
+  }
+
+  public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache {
+    private readonly IGeodeCache _cache;
+    private ILogger<GeodeSessionStateCache> _logger;
+    private static Region _region;
+    private string _logLevel;
+    private string _logFile;
+    private string _regionName;
+    private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+    public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+      var host = optionsAccessor.Value.Host;
+      var port = optionsAccessor.Value.Port;
+      _regionName = optionsAccessor.Value.RegionName;
+      _logLevel = optionsAccessor.Value.LogLevel;
+      _logFile = optionsAccessor.Value.LogFile;
+
+      _cache = CacheFactory.Create()
+                   .SetProperty("log-level", _logLevel)
+                   .SetProperty("log-file", _logFile)
+                   .CreateCache();
+
+      _cache.PoolManager.CreatePoolFactory().AddLocator(host, port).CreatePool("pool");
+
+      var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+      _region = regionFactory.CreateRegion(_regionName);
+    }
+
+    // Returns the SessionStateValue for key, or null if key doesn't exist
+    public GeodeSessionStateValue GetValueForKey(string key) {
+      byte[] cacheValue = _region.GetByteArray(key);

Review comment:
       `var`

##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session {
+  public class GeodeSessionStateValue {
+    //DateTime _lastAccessTimeUtc;
+    //DateTime _expirationTimeUtc = DateTime.MinValue;
+    //TimeSpan _spanUntilStale = TimeSpan.Zero;
+    //private byte[] _value;
+
+    public GeodeSessionStateValue() {}
+    public GeodeSessionStateValue(byte[] value) {
+      FromByteArray(value);
+    }
+
+    public byte[] Value { get; set; }
+    public DateTime LastAccessTimeUtc { get; set; }
+    public DateTime ExpirationTimeUtc { get; set; } = DateTime.MinValue;
+    public TimeSpan SpanUntilStale { get; set; } = TimeSpan.Zero;
+
+    public byte[] ToByteArray() {
+      int neededBytes = 3 * sizeof(long) + Value.Length;
+      byte[] byteArray = new byte[neededBytes];
+      int byteIndex = 0;
+
+      Array.Copy(BitConverter.GetBytes(LastAccessTimeUtc.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(BitConverter.GetBytes(ExpirationTimeUtc.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(BitConverter.GetBytes(SpanUntilStale.Ticks), 0, byteArray, byteIndex,
+                 sizeof(long));
+      byteIndex += sizeof(long);
+
+      Array.Copy(Value, 0, byteArray, byteIndex, Value.Length);
+      return byteArray;
+    }
+
+    public void FromByteArray(byte[] data) {
+      int byteIndex = 0;
+
+      LastAccessTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      ExpirationTimeUtc = DateTime.FromBinary(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      SpanUntilStale = TimeSpan.FromTicks(BitConverter.ToInt64(data, byteIndex));
+      byteIndex += sizeof(long);
+
+      Value = new byte[data.Length - byteIndex];
+      Array.Copy(data, byteIndex, Value, 0, data.Length - byteIndex);
+    }
+  }
+
+  public class GeodeSessionStateCache : GeodeNativeObject, IDistributedCache {
+    private readonly IGeodeCache _cache;
+    private ILogger<GeodeSessionStateCache> _logger;
+    private static Region _region;
+    private string _logLevel;
+    private string _logFile;
+    private string _regionName;
+    private readonly SemaphoreSlim _connectLock = new SemaphoreSlim(initialCount: 1, maxCount: 1);
+
+    public GeodeSessionStateCache(IOptions<GeodeSessionStateCacheOptions> optionsAccessor) {
+      var host = optionsAccessor.Value.Host;
+      var port = optionsAccessor.Value.Port;
+      _regionName = optionsAccessor.Value.RegionName;
+      _logLevel = optionsAccessor.Value.LogLevel;
+      _logFile = optionsAccessor.Value.LogFile;
+
+      _cache = CacheFactory.Create()
+                   .SetProperty("log-level", _logLevel)
+                   .SetProperty("log-file", _logFile)
+                   .CreateCache();
+
+      _cache.PoolManager.CreatePoolFactory().AddLocator(host, port).CreatePool("pool");
+
+      var regionFactory = _cache.CreateRegionFactory(RegionShortcut.Proxy);
+      _region = regionFactory.CreateRegion(_regionName);
+    }
+
+    // Returns the SessionStateValue for key, or null if key doesn't exist
+    public GeodeSessionStateValue GetValueForKey(string key) {
+      byte[] cacheValue = _region.GetByteArray(key);
+
+      if (cacheValue != null) {
+        return new GeodeSessionStateValue(cacheValue);
+      } else
+        return null;
+    }
+
+    public byte[] Get(string key) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      Connect();
+
+      // Check for nonexistent key
+      GeodeSessionStateValue ssValue = GetValueForKey(key);
+      if (ssValue == null) {
+        return null;
+      }
+
+      // Check for expired key
+      DateTime nowUtc = DateTime.UtcNow;
+      if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc) {
+        return null;
+      }
+
+      // Check for stale key
+      if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+          nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale)) {
+        return null;
+      }
+
+      // Update the times for sliding expirations
+      if (ssValue.SpanUntilStale != TimeSpan.Zero) {
+        ssValue.LastAccessTimeUtc = nowUtc;
+        _region.PutByteArray(key, ssValue.ToByteArray());
+      }
+
+      return ssValue.Value;
+    }
+
+    public Task<byte[]> GetAsync(string key, CancellationToken token = default(CancellationToken)) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      token.ThrowIfCancellationRequested();
+
+      return Task.Factory.StartNew(() => Get(key), token);
+    }
+
+    public void Refresh(string key) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      Connect();
+
+      // Check for nonexistent key
+      GeodeSessionStateValue ssValue = GetValueForKey(key);
+      if (ssValue == null) {
+        return;
+      }
+
+      // Check for expired key
+      var nowUtc = DateTime.UtcNow;
+      if (ssValue.ExpirationTimeUtc != DateTime.MinValue && ssValue.ExpirationTimeUtc < nowUtc) {
+        return;
+      }
+
+      // Check for stale key
+      if (ssValue.SpanUntilStale != TimeSpan.Zero &&
+          nowUtc > (ssValue.LastAccessTimeUtc + ssValue.SpanUntilStale)) {
+        return;
+      }
+
+      // Update the times for sliding expirations
+      if (ssValue.SpanUntilStale != TimeSpan.Zero) {
+        ssValue.LastAccessTimeUtc = nowUtc;
+        _region.PutByteArray(key, ssValue.ToByteArray());
+      }
+    }
+
+    public Task RefreshAsync(string key, CancellationToken token = default(CancellationToken)) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      token.ThrowIfCancellationRequested();
+
+      return Task.Factory.StartNew(() => Refresh(key), token);
+    }
+
+    public void Remove(string key) {
+      if (key == null) {
+        throw new ArgumentNullException(nameof(key));
+      }
+
+      Connect();
+
+      // Until we return error codes

Review comment:
       More commented out code to delete.

##########
File path: netcore/NetCore.Session/NetCoreSessionState.cs
##########
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using Apache.Geode.Client;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Apache.Geode.Session {
+  public class GeodeSessionStateValue {
+    //DateTime _lastAccessTimeUtc;

Review comment:
       Delete commented out code.

##########
File path: netcore/NetCore.Session.IntegrationTests/NetCore.Session.IntegrationTests.csproj
##########
@@ -0,0 +1,26 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
+
+    <IsPackable>false</IsPackable>
+
+    <Platforms>x64</Platforms>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
+    <PackageReference Include="xunit" Version="2.4.1" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
+      <PrivateAssets>all</PrivateAssets>

Review comment:
       Strikes me that all the test assets are all the same, so why is the runner special?




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@geode.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org