<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Armin Shoeibi</title>
    <description>The latest articles on DEV Community by Armin Shoeibi (@arminshoeibi).</description>
    <link>https://dev.to/arminshoeibi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1667588%2Fb9f2d10d-77f3-4a91-958f-50678d159383.jpg</url>
      <title>DEV Community: Armin Shoeibi</title>
      <link>https://dev.to/arminshoeibi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arminshoeibi"/>
    <language>en</language>
    <item>
      <title>What is GCHandle in C#? (Part 1)</title>
      <dc:creator>Armin Shoeibi</dc:creator>
      <pubDate>Sun, 05 Jan 2025 22:34:26 +0000</pubDate>
      <link>https://dev.to/arminshoeibi/what-is-gchandle-in-c-part-1-243j</link>
      <guid>https://dev.to/arminshoeibi/what-is-gchandle-in-c-part-1-243j</guid>
      <description>&lt;p&gt;What is a &lt;em&gt;&lt;strong&gt;WeakReference&lt;/strong&gt;&lt;/em&gt; in C#? To fully understand it, we first need to delve into the &lt;strong&gt;internals&lt;/strong&gt; of the platform, specifically &lt;strong&gt;&lt;em&gt;GCHandle&lt;/em&gt;&lt;/strong&gt;. Understanding GCHandle is crucial before we explore &lt;code&gt;WeakReference&lt;/code&gt; and &lt;code&gt;WeakReference&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;GCHandle is a struct that helps us communicate with the .NET runtime (mostly GC) using P/Invoke. It’s been around since .NET Framework 1.1, but these days, it uses the modern LibraryImport instead of the older DllImport.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://github.com/dotnet/runtime/blob/v9.0.0/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs" rel="noopener noreferrer"&gt;GCHandle.cs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dotnet/runtime/blob/v9.0.0/src/mono/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.Mono.cs" rel="noopener noreferrer"&gt;GCHandle.Mono.cs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dotnet/runtime/blob/v9.0.0/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.cs" rel="noopener noreferrer"&gt;GCHandle.CoreCLR.cs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dotnet/runtime/blob/v9.0.0/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.NativeAot.cs" rel="noopener noreferrer"&gt;GCHandle.NativeAot.cs&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to see them all at a glance &lt;a href="https://github.com/search?q=repo%3Adotnet%2Fruntime+%22struct+GCHandle%22&amp;amp;type=code" rel="noopener noreferrer"&gt;click&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is a &lt;code&gt;partial struct&lt;/code&gt; with 2 properties.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You can't instantiate it using the &lt;code&gt;new&lt;/code&gt; keyword.&lt;/li&gt;
&lt;li&gt;You can new it up by calling the &lt;code&gt;Alloc&lt;/code&gt; method, it accepts 2 parameters.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Alloc&lt;/code&gt; method needs the object reference and the type of Handle.&lt;/li&gt;
&lt;li&gt;It has four types: &lt;strong&gt;Weak, WeakTrackResurrection, Normal, or Pinned&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Some of its methods are internal and not available for use in our applications.&lt;/li&gt;
&lt;li&gt;It uses &lt;code&gt;System.IntPtr&lt;/code&gt; or &lt;code&gt;nint&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It employs the &lt;code&gt;LibraryImport&lt;/code&gt; attribute, a new way of using &lt;code&gt;DllImport&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It uses the &lt;code&gt;extern&lt;/code&gt; keyword.&lt;/li&gt;
&lt;li&gt;It involves the use of &lt;code&gt;pointers&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It utilizes the &lt;code&gt;MethodImpl&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;it calls this &lt;a href="https://github.com/dotnet/runtime/blob/v9.0.0/src/coreclr/vm/marshalnative.cpp#L348" rel="noopener noreferrer"&gt;C++ code&lt;/a&gt; &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Enough theory, Let's write some code with &lt;code&gt;Weak&lt;/code&gt;Type and check the available properties and methods.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a Customer object.&lt;/li&gt;
&lt;li&gt;Create a GCHandle with a &lt;code&gt;Weak&lt;/code&gt; type and reference to the customer.&lt;/li&gt;
&lt;li&gt;Print the &lt;em&gt;allocation status&lt;/em&gt; and check if the &lt;em&gt;target is not null&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Retrieve and print the IntPtr(address) representation of the GCHandle.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;iframe src="https://dotnetfiddle.net/Widget/UqE0U7" width="100%" height="600"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Let's go and check it out in a real-world situation.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create&lt;/strong&gt; an object from Customer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a Weak GCHandle&lt;/strong&gt; for the Customer object.&lt;/li&gt;
&lt;li&gt;*&lt;em&gt;Print *&lt;/em&gt; the customer object and leave the scope (&lt;code&gt;CreateCustomerAndPrintIt&lt;/code&gt; method).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Force GC&lt;/strong&gt; to collect unreferenced objects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the Target property&lt;/strong&gt; of GCHandle it should be null if the Customer has been garbage collected.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;iframe src="https://dotnetfiddle.net/Widget/dGI8eO" width="100%" height="600"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Tip: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;After garbage collection&lt;/em&gt;, the &lt;strong&gt;Target property of a GCHandle&lt;/strong&gt; can become null because the object has been collected. However, &lt;a href="https://github.com/dotnet/runtime/blob/v9.0.0/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs#L147" rel="noopener noreferrer"&gt;IsAllocated stays true because it only checks if the handle field&lt;/a&gt; (a memory address) is non-zero. The memory address doesn’t reset, even if the target object is gone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You might be wondering, "So what?" 😁 What did we solve? We'll see soon enough. &lt;br&gt;
To give you a quick hint: &lt;strong&gt;caching&lt;/strong&gt;! It helps us cache references without causing memory leaks or consuming too much memory in critical applications.&lt;/p&gt;

&lt;p&gt;Want something more to think about? I'll tell you: &lt;code&gt;ConditionalWeakTable&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt; uses the same infrastructure.&lt;/p&gt;

&lt;p&gt;Let's explore other types of GCHandle in the next post. &lt;code&gt;(WeakTrackResurrection, Normal and Pinned)&lt;/code&gt;&lt;/p&gt;

</description>
      <category>gc</category>
      <category>dotnet</category>
      <category>memory</category>
      <category>malloc</category>
    </item>
    <item>
      <title>Real Graceful Shutdown in Kubernetes and ASP.NET Core</title>
      <dc:creator>Armin Shoeibi</dc:creator>
      <pubDate>Sat, 22 Jun 2024 19:31:52 +0000</pubDate>
      <link>https://dev.to/arminshoeibi/real-graceful-shutdown-in-kubernetes-and-aspnet-core-2290</link>
      <guid>https://dev.to/arminshoeibi/real-graceful-shutdown-in-kubernetes-and-aspnet-core-2290</guid>
      <description>&lt;p&gt;Our team recently developed a "Payment as a Service" solution for our company. This service aims to provide a seamless payment integration for other microservices. We built it using an ASP.NET Core 8 and deployed it on Kubernetes (K8s).&lt;/p&gt;

&lt;p&gt;However, &lt;u&gt;we've faced significant stress during deployments&lt;/u&gt;. We often had to stay up late to perform near-rolling updates. The process wasn't a true rolling update, and it caused us considerable frustration.&lt;/p&gt;

&lt;p&gt;Initially, our application had a 30-second graceful shutdown period. You might ask how? This is because &lt;strong&gt;.NET's Generic Host&lt;/strong&gt; defaults the &lt;code&gt;ShutdownTimeout&lt;/code&gt; to 30 seconds. However, this default setting wasn't suitable for our application, as we had long-running tasks and API calls.&lt;/p&gt;

&lt;p&gt;We increased the shutdown timeout to 90 seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;builder.Host.ConfigureHostOptions(ho =&amp;gt; 
{
    ho.ShutdownTimeout = TimeSpan.FromSeconds(90);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but we still experienced several &lt;strong&gt;SIGKILLs&lt;/strong&gt; after 30 seconds during our rolling updates. Initially, Kubernetes sends a &lt;strong&gt;SIGTERM&lt;/strong&gt; signal, giving the pod 30 seconds to stop and shut down. However, our pods needed up to 90 seconds, not 30 seconds.&lt;/p&gt;

&lt;p&gt;To address this, we needed to configure this behavior in Kubernetes. After some research, we discovered the &lt;code&gt;terminationGracePeriodSeconds&lt;/code&gt; setting, which defaults to 30 seconds and was causing the SIGKILLs. We set it to 120 seconds &lt;em&gt;thirty seconds&lt;/em&gt; more than our application's maximum shutdown needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apiVersion: apps/v1
kind: Statefulset
metadata:
  name: ---
spec:
  containers:
  - name: ---
    image: ---
    terminationGracePeriodSeconds: 120
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far, we've made two key changes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Increased the &lt;code&gt;HostOptions.ShutdownTimeout&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Increased the &lt;code&gt;terminationGracePeriodSeconds&lt;/code&gt; in the k8s manifest&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After making these changes, we tested our application and everything worked flawlessly.&lt;/p&gt;

&lt;p&gt;To validate these changes, we created a straightforward action method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Route("api/v1/graceful-shutdown")]
[ApiController]
public class GracefulShutdownController : ControllerBase
{
    public async Task&amp;lt;IActionResult&amp;gt; TestAsync()
    {
        await Task.Delay(TimeSpan.FromSeconds(75));
        return Ok();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We called the 'TestAsync' endpoint and &lt;u&gt;immediately deployed a new version using Kubernetes&lt;/u&gt;. Our pod entered the terminating state with a 120-second grace period provided by Kubernetes, while our application's shutdown timeout was set to 90 seconds. The 'TestAsync' action method, designed to run for 75 seconds, executed smoothly during this transition.&lt;/p&gt;

&lt;p&gt;However, after several updates, our downstream microservices—mostly front-end applications—reported issues where some of their HTTP calls failed during our rolling updates. After further investigation, we discovered a gap between the Nginx Ingress controller and the pod states.&lt;/p&gt;

&lt;p&gt;We found issues on GitHub related to this, and the .NET team fixed it by replacing &lt;strong&gt;IHostLifetime&lt;/strong&gt; with a new implementation that delays the SIGTERM signal.&lt;br&gt;
We set the delay to &lt;strong&gt;10 seconds&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System.Runtime.InteropServices;

namespace OPay.API.K8s;

public class DelayedShutdownHostLifetime(IHostApplicationLifetime applicationLifetime) : IHostLifetime, IDisposable
{
    private IEnumerable&amp;lt;IDisposable&amp;gt;? _disposables;

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }

    public Task WaitForStartAsync(CancellationToken cancellationToken)
    {
        _disposables =
        [
            PosixSignalRegistration.Create(PosixSignal.SIGINT, HandleSignal),
            PosixSignalRegistration.Create(PosixSignal.SIGQUIT, HandleSignal),
            PosixSignalRegistration.Create(PosixSignal.SIGTERM, HandleSignal)
        ];
        return Task.CompletedTask;
    }

    protected void HandleSignal(PosixSignalContext ctx)
    {
        ctx.Cancel = true;
        Task.Delay(TimeSpan.FromSeconds(10)).ContinueWith(t =&amp;gt; applicationLifetime.StopApplication());
    }

    public void Dispose()
    {
        foreach (var disposable in _disposables ?? Enumerable.Empty&amp;lt;IDisposable&amp;gt;())
        {
            disposable.Dispose();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then register this Impl in the IoC.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;builder.Services.AddSingleton&amp;lt;IHostLifetime, DelayedShutdownHostLifetime&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find the main source of the above code from &lt;a href="https://github.com/dotnet/dotnet-docker/blob/main/samples/kubernetes/graceful-shutdown/graceful-shutdown.md#adding-a-shutdown-delay"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After implementing this shutdown delay, we eliminated deployment-related issues and significantly reduced our stress levels.&lt;/p&gt;

&lt;p&gt;Navigate through these links to learn more:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/dotnet/dotnet-docker/blob/main/samples/kubernetes/graceful-shutdown/graceful-shutdown.md#adding-a-shutdown-delay"&gt;https://github.com/dotnet/dotnet-docker/blob/main/samples/kubernetes/graceful-shutdown/graceful-shutdown.md#adding-a-shutdown-delay&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/dotnet/runtime/blob/v8.0.6/src/libraries/Microsoft.Extensions.Hosting/src/HostOptions.cs"&gt;https://github.com/dotnet/runtime/blob/v8.0.6/src/libraries/Microsoft.Extensions.Hosting/src/HostOptions.cs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/dotnet/runtime/blob/v8.0.6/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConsoleLifetime.netcoreapp.cs"&gt;https://github.com/dotnet/runtime/blob/v8.0.6/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ConsoleLifetime.netcoreapp.cs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/dotnet/runtime/blob/v8.0.6/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs#L235"&gt;https://github.com/dotnet/runtime/blob/v8.0.6/src/libraries/Microsoft.Extensions.Hosting/src/Internal/Host.cs#L235&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/dotnet/runtime/blob/v8.0.6/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ApplicationLifetime.cs"&gt;https://github.com/dotnet/runtime/blob/v8.0.6/src/libraries/Microsoft.Extensions.Hosting/src/Internal/ApplicationLifetime.cs&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host?tabs=appbuilder#hosting-shutdown-process"&gt;https://learn.microsoft.com/en-us/dotnet/core/extensions/generic-host?tabs=appbuilder#hosting-shutdown-process&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>k8s</category>
      <category>dotnet</category>
      <category>cloudnative</category>
      <category>kubernetes</category>
    </item>
  </channel>
</rss>
