It's PI Day...

PISo it is PI Day and I thought I would write something up about PI.

First, what is PI?  PI is a number that is the exact ratio between the diameter and the circumference of a circle or the distance across to the distance around.  PI is an irrational number meaning that its precise number in decimal notation is infinitely long and will never repeat.  So PI is the best way to get a glimpse into the mind of God and how He designed this grand part of the world.

Second, PI... is it 3.14 or is it 3.14159 and what does that matter?  This comes into accuracy and precision.  Accuracy is the closeness of a measured value to a standard, think of hitting a bullseye.  Precision is the closeness of measurements to each other, think of grouping all of the hits on a bullseye near each other but not on the bullseye.  When a measurement is both precise and accurate then it will would be the center of a bullseye.  The greater number of decimals used with PI would increase the accuracy of the number.

So it would appear that using a more accurate number of PI would be beneficial, but when you start using highly accurate numbers with computers issues with data types begin to come into play.  Depending on the data type, the processor on the computer will have optimization for doing the calculations.  Float for example is used for high performance but isn't accurate.  This would mean that even if a highly accurate number with PI is used with a float will not generate precise or accurate numbers.  

So how accurate is too accurate?  Well, suffice to say there is a certain point of diminishing returns for using a highly accurate number of PI.  So according the the Jet Propulsion Lab, fifteen decimal places will calculate the a circle that is 78 billion miles around with the accuracy of 1.5 inches.  If PI with fifteen decimal places is used to calculate a circle that can fit on the earth, it will have an accuracy to within one molecule.  Forty-six decimal places of PI is able to calculate a circle to an accuracy of less than the diameter of a hydrogen atom for a circle that would just fit within the entire visible universe.

So the reality of the world is, 3.14 is a great number and a precise number for every day life.

inotify Instances .Net Core on Linux

Now you get the exception "The configured user limit (128) on the number of inotify instances has been reached" and you wonder what the heck is going on.  For me I never got this exception on my local instance of Docker running on my Mac or in Windows but I would see my app just stop running in Linux.  Even weirder I could run the app fine in Windows hosted by Kestrel.  So where does this error start coming from?  

After much digging, I found the problem was with the IConfiguration interface.  Specificially, I was calling pulling the JSON configuration files multiple times which was reloading the IConfiguration interface.  The issue was that it was rebuilding the configuration and re-attaching the "reloadOnChange" setting.  Since the reloadOnChange was set to true it was attaching a new file watch multiple times and was hitting the limit.  That all being said, I never saw this issue locally on my Mac or Windows or my local instances of Docker on either which I think is related to the way the underlying Docker Linux host manages the watch on the files.  

Something else as a side note, since the files were being tracked, even though I was disposing of the class it wasn't releasing the watch and thus keeping these connections open.  The simple fix was to set the reloadOnChange to false but to make it even better was to just create a singleton of the data and thus prevent the data from being ever reloaded.  I don't think it is necessarily a bug with .Net but it is a weird side effect of how the host can cause problems with a framework.  

I hope this helps some people save some time that I lost dealing with this crazy issue.

 

Sending Mass Emails in C#

I like many developers am tasked with creating batch emails to send to a large population of users.  And for this I am not talking about spam, I am referring to sending emails to a large poplulation within an organization such as annual notification reminders or custom links to some application.  I have tried sending these emails synchroniously and if you are sending a few hundred that isn't a problem, but when I started sending several thousand, I was noticing a bottleneck with my SMTP server and waiting for the callback showing the SMTP server receved the message.  

So moving forward from that thought the best option for sending mass emails greater than a few hundred is using threading and asynchronious sending.  I have tried this a few different ways in the past with event listeners and different techniques, but moving to the async and await world of modern .Net I thought I would re-write it with some more modern tooling.  Since this took me a little trial and error I thought it would be a good thing to post for my fellow developers out there. 

From a performance standpoint using the same SMTP server I was getting about three emails per second with standard synchronous methods and when I went to the attached code, I went up to about 18 (running locally over VPN to our corporate offices) and I will expect to see an even bigger jump on a VM in the datacenter.  Overall there isn't much impact to performance on the machine with minimal CPU and RAM overhead.  

Anyway, click here to get to my code.  

Two things I want to point out:

  1. I know I have a weird try / catch in there.  That is because the mail server was choking with that many emails going through at one time and I was getting random errors.  Retrying one time usually fixed it.
  2. I know there are async methods with the SMTP client, but when you use that it still only allows one email to be processed at a time by the general SMTP client thus making it an async method but not async in the since that I wanted which was mass emails with threading.

o365 Groups, My Hatred of it

I hate PowerShell, I hate o365 (more specifically o365 Groups / Outlook Groups), and I hate SharePoint in the Office 365 Tenant. 

I know that is a strong way to start off a statement on managing o365 Groups but after banging my head against the wall for too long around o365 Groups I can’t think of a more appropriate statement regarding my feelings for Microsoft’s new product line.  So let me break down my frustration and give a few insights.

I am going to start with my hatred of SharePoint.  First off, the platform is a pig and it is a bloated pig, but I am not going to get into that right now.  With Microsoft’s new direction for SharePoint in building other products on top of it such as o365 Groups or Planner, Microsoft is stepping in the world of SharePoint and making a muck of the SharePoint environment with other products that trample over the storage and URL-space of the SharePoint tenant.  So when you create an o365 Group or a Planner plan, then you have created a SharePoint site collection in the tenant that you don’t see in the dashboard or have direct control over the quota (without PowerShell). 

Moving into my hatred of o365 Groups.  Here is a product that Microsoft is building as a collaborative communication tool (what about Yammer) and they are encouraging users to create their own groups, but they have little governance on how the groups are created (and that is just coming in now).  One exciting thing about groups is that the users can create whatever they want… and guess what, it shows up in the GAL.  A user could effectively create a group saying “Union Formation Group” and what could the company do to stop it, they could create a lot of legal problems in the company instantly and there is no way to really stop it short of turning off Groups.  Keep in mind that these Groups use quota from SharePoint too.  Here is just another though on Groups, the name.  We have AD Groups, Azure Groups, o365 Groups, just group Groups, and where will this end.  Let’s stop using generic names for products (Word?) which causes so much confusion when using the Google to try to return information (“the Google” was not a typo, it is a being now).

Lastly, I am going to complain about PowerShell or more specifically the implementation of PowerShell and the documentation for the o365 Groups.  Microsoft’s documentation is scattered and not clear on top of the fact that when you search for “Office 365 Group” or “o365 Group” you get everything from groups about o365 to permission groups in SharePoint (back to naming).  Even when you find the documentation that shows things like optional parameters for the PowerShell it is very incomplete.  For example, many of the options that are available on “Set-UnifiedGroup” (did you notice that one?  All of a sudden an o365 Group is called a “Unified Group”) are available too on “New-UnifiedGroup” even though it doesn’t show up there in the documentation.  Here is another fun one, if you want to set the quota for the file storage, it is a SharePoint PowerShell command and does not involve the “Unified Group.”  My favorite of all, I was trying to interact with the “Description” field for the o365 Group but that is the “Notes” field in PowerShell, what the hell?  Why do you even use named properties when you change the meaning of the names or the names in the UI. 

Well, there was my rant.  I doubt there will be anything helpful, but if you want the complete current list of PowerShell parameters I have it below.  Please look back in a few weeks for some examples on connecting to PowerShell through C# to create a new Unified Group or whatever the hell Microsoft changes the name to next.

 

  • New-UnifiedGroup
    • [-AccessType <Public | Private>]
    • [-Alias <String>]
    • [-AutoSubscribeNewMembers <SwitchParameter>]
    • [-Confirm [<SwitchParameter>]]
    • [-Database <DatabaseIdParameter>]
    • [-DisplayName <String>]
    • [-DomainController <Fqdn>]
    • [-EmailAddresses <ProxyAddressCollection>]
    • [-ExecutingUser <RecipientIdParameter>]
    • [-ExternalDirectoryObjectId <String>]
    • [-FromSyncClient <SwitchParameter>]
    • [-Language <CultureInfo>]
    • [-ManagedBy <RecipientIdParameter[]>]
    • [-Members <RecipientIdParameter[]>]
    • [-Name <String>]
    • [-Notes <String>]
    • [-Organization <OrganizationIdParameter>]
    • [-OrganizationalUnit <OrganizationalUnitIdParameter>]
    • [-OverrideRecipientQuotas <SwitchParameter>]
    • [-PrimarySmtpAddress <SmtpAddress>]
    • [-RecipientIdType <Unknown | ExternalId | Smtp | LegacyDn>]
    • [-RequireSenderAuthenticationEnabled <$true | $false>]
    • [-SharePointResources <MultiValuedProperty>]
    • [-SuppressWarmupMessage <SwitchParameter>]
    • [-ValidationOrganization <String>]
    • [-WhatIf [<SwitchParameter>]]
    • [<CommonParameters>]