Sunday, October 31, 2021

A joyful programming episode

On the joy of writing a mix of code with PowerShell, YAML, .NET, XML, and XPath. It was a lot of fun.

The task at hand is to write a simple preliminary version of a PowerShell script to create some AWS CloudWatch LogGroups and related LogStreams. The input is an AWS CloudFormation template YAML file. This template file helped before to create the required CloudWatch resources, but now the need is to create the same resources by calls to the AWS CloudWatch Logs API. At execution time, at an AWS EC2 instance, the required PowerShell 7 script will call AWS CloudWatch Logs API by means of the AWS Tools for PowerShell already installed at such EC2 instance, which has attached an AWS IAM Instance Profile with the required permissions.

Curious thing, though, the PowerShell Cmdlets to parse and query the YAML template data were not found as quickly as the Cmdlets to parse and query other common file formats (JSON, XML, CSV, etc.).

PowerShell 7 can load and execute code in .NET Assemblies (given the proper match on targeted .NET CLR). There is, e.g., YamlDotNet package to parse YAML files, but an initial try of it directly from PowerShell was not as straightforward as expected. Nevertheless, that same package could be used from a custom .NET Assembly that would render the YAML content transposed as another serialization format already known to familiar PowerShell Cmdlets. The first use case for such custom .NET Assembly would be like the following test case in an executable functional specification:

 

The System Under Test (SUT) is the .NET class named yaml, which encapsulates the access to a YAML parser (deserial method) and to the rendition of the CloudWatch Logs resources data transposed as XML serialization format version 1.0 (AsXml_1dot0 method). This AsXml_1dot0 method can be wrapped by a .NET Console host and invoked from a PowerShell script. The output of that call to the AsXml_1dot0 method can be parsed and queried by Select-Xml Cmdlet.

Next is a general idea for the .NET Console host wrapper for the AsXml_1dot0 method:

 

Before getting into the general layout for the required PowerShell script (Create-CloudWatchLogs.ps1), first let’s see next a basic layout for its invocation and its output:

 

Also, next is the related input file CloudWatchLogs.yml (an AWS CloudFormation template YAML file) with the data for the resources to be created:

 

Now, the executable functional specification, so far, includes the case to transpose from a YAML map data type to XML. The YAML file in this case contains only map type instances, but for the case of YAML seq data type, next is an initial test case into the executable functional specification:

 

And, next is an initial test case for the case of YAML str data type:

 

Furthermore, next is a test case for YAML map and YAML seq data types combined:

 

Next is the general layout of the required PowerShell script Create-CloudWatchLogs.ps1:

 

I especially enjoyed the craft of those XPath expressions; it has been a while since my last time with XPath.

Finally, next is the current initial implementation of the yaml class with the parsing and transposition of YAML into XML:

 

Saturday, June 26, 2021

Know your design tools — The Performing Half-baked object case

Among other important paradigms in software design, object orientation is a common set of design techniques these days. Hierarchies of classes, derivative class instantiations and virtual calls can be often found on software codebases. Hence, the relevance of the Performing Half-baked object case while designing object-oriented software.

The Performing Half-baked object case occurs where a virtual call is executed onto an unborn or uncreated or uninitialized class instance: the actual behavior can be unexpected, of course.

A couple of examples.

Consider the output of this Windows/Visual C++ code:

#include <iostream>

class AA
{
    public:
    AA()
    {
        std::cout << "AA()\n";
        f();
    }
    virtual void f() { std::cout << "AA.f\n"; }
};

class BB : public AA
{
    public:
    BB()
    {
        std::cout << "BB()\n";
        f();
    }
    virtual void f() { std::cout << "BB.f\n";}
};

void main()
{
	auto x = new BB;
}

For another –different– behavior, consider the output of this .NET/Visual C# code:

using static System.Console;

class AA
{
    public AA()
    {
        WriteLine("AA()");
        f();
    }
    public virtual void f() => WriteLine("AA.f");
}

class BB : AA
{
    public BB()
    {
        WriteLine("BB()");
        f();
    }
    public override void f() => WriteLine("BB.f");
}

class Program
{
    public static void Main()
    {
        var x = new BB();
    }
}

The Performing Half-baked object is a case that designers of object-oriented languages and runtimes have typically faced. Any designer using those languages and runtimes should be aware of this and other cases during the process of object creation.

Singleton in .NET

If I need a process-level global write-once/read-many state, a global variable, a Singleton, in .NET Runtime, then I keep in mind what I said at the post: Know your design tools — The Singleton case.

It is also relevant to keep in mind:

  1. the Visual C# language specification about Static constructors,

  2. the section on Static Constructors of the Visual C# Programming Guide,

  3. the description of what BeforeFieldInit does, and

  4. the category (Performance) and the 'When to suppress warnings' section of the CA1810 Rule.

The observations on that old post have been useful for me over the years. Specially while designing stateful software services, where the use of locking, global state, global variables, or Singletons do not hinder throughput. On the other hand, in the context of stateless software services processing a high number of concurrent requests, whose scalability is very important, then –to be clear–, I do not prefer any explicit use of locking, any global state, any global variables or any Singleton.

Here is a demonstrative version of a .NET/Visual C#-based Singleton derived from the sample code of such old post and a couple of MSTest test cases as evidence of its correctness, included post-conditions for same class instance and for same global value:

#region SUT
/// <summary>
///  https://docs.microsoft.com/en-us/archive/blogs/marcod/know-your-design-tools-the-singleton-case
///  https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/classes#static-constructors
///  https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors
///  https://docs.microsoft.com/en-us/dotnet/api/system.reflection.typeattributes?view=net-5.0
///  https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1810
/// </summary>
public sealed class TimepointSingleton
{
    #region Nested static class with access to private outer instance constructor.
    public static class Timepoint
    {
        private static readonly TimepointSingleton instance;

        static Timepoint()
        {
            instance = new TimepointSingleton();
        }

        public static TimepointSingleton Instance { get => instance; }
    }
    #endregion

    #region Outer class for instance-level members of Singleton.
    private DateTime point;

    private TimepointSingleton()
    {
        point = DateTime.Now;
        WriteLine("Timepoint acquired");

        //This simulates resource contention to better
        //show up the multithreading problem when no
        //proper thread synchronization is in place.
        Thread.Sleep(4000);
    }

    public DateTime GlobalTimepoint { get => point; }

    public override string ToString() => $"{GlobalTimepoint:s}";
    #endregion
}
#endregion

Test cases (MSTest) as evidence of the expected and actual behavior:

[TestClass]
public class SingletonSpec
{
    [TestMethod]
    public void two_reads()
    {
        //Arrange
        TimepointSingleton a = null;
        TimepointSingleton b = null;

        //Act
        a = TimepointSingleton.Timepoint.Instance;
        b = TimepointSingleton.Timepoint.Instance;

        //Assert
        Assert.IsNotNull(a);
        Assert.IsTrue(AreTheseTheExactSameObject(a, b));
        Assert.AreEqual($"{a}", $"{b}");

        static bool AreTheseTheExactSameObject(object a, object b) => object.ReferenceEquals(a, b);
    }

    [TestMethod]
    public void concurrent_reads()
    {
        //Arrange
        int read_count = 15;
        using var sync = new ManualResetEvent(false);
        var reads = new ConcurrentBag<string>();
        var instances = new ConcurrentBag<TimepointSingleton>();
        var threads = Enumerable.Range(0, read_count).Aggregate(new List<Thread>(), (whole, next) => { whole.Add(new Thread(operation)); return whole; });

        //Act
        threads.ForEach(t => t.Start());
        sync.Set();
        threads.ForEach(t => t.Join());

        //Assert
        Assert.AreEqual(read_count, reads.Count);
        Assert.IsTrue(reads.All(read => string.CompareOrdinal(read, $"{TimepointSingleton.Timepoint.Instance.GlobalTimepoint:s}") == 0));
        Assert.AreEqual(read_count, instances.Count);
        Assert.IsTrue(instances.All(instance => object.ReferenceEquals(instance, TimepointSingleton.Timepoint.Instance)));

        void operation()
        {
            WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] {DateTime.Now:s} Started");
            sync.WaitOne();
            var global_state = TimepointSingleton.Timepoint.Instance;
            reads.Add($"{global_state}");
            instances.Add(global_state);
            WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] {DateTime.Now:s} Instance value: {global_state} hash: {global_state.GetHashCode()}");
        }
    }
}