Categories
.NET

Newtonsoft/System.Text.Json Testing

Microsoft has released NET Core 3 and with that included System.Text.Json a new json serializer to replace Newtonsoft. One of the claims is that This new serializer is faster. There is already multiple benchmarks proving that claim. However I wanted to see what the difference actually became in real life against one of my existing APIs.

End to end testing

I will do an E2E testing using ab (Apache Benchmark) to see if there will be a difference in response time and how many simultaneous request the api can handle. I will test everything 10 times and then average out the value.

To test the the response time i will do one single request with ab and se how long it takes.

ab -n 1 -c 1 "https://localhost:5001/v3/api/"

To test how many request per second i can handle i will use ab to send 100 simultaneous request to the server 5 times.

ab -n 500 -c 100 "https://localhost:5001/v3/"

Test 1

The first test is fetching a single item from the API. The payload is 1,7 KB, the request in is fetching the data IDistributedCache, most of the work is therefor deserialize the data from IDistributedCache and then serialize it again for the response.

Response time 1/reqRequest per second
Newtonsoft14,8 ms98,7 req/s
System.Text.Json 18,4 ms100,2 req/s

Test 2

The second test fetches 48 items from the api, the payload is 36,6 KB. Most of the time is spent on fetching data from the database


Response time 1/req

Request per second
Newtonsoft 129,8 ms26,11 req/s
System.Text.Json 123,5 ms
31,3 req/s

Test 2b

I found the result of test 2 interesting and decided to redo the test but increase the total amount of request from 500 to 5000.

Request per second
Newtonsoft 19,4 req/s
System.Text.Json 30,53 req/s

Conclusion

I am a bit amazed on how big the difference became, for test 2. looking into some other tests there actually might be something behind it. quoting from the comments at The Battle of C# to JSON Serializers in .NET Core 3

Up to the initial buffer size (8-16kb depending on the lib)? Nothing, they all pretty much behave the same, the buffer is filled and after the serialization is done, the buffer is flushed to the output pipe/stream.
After that size it gets interesting. System.Text.Json is capable of flushing the data and reusing the old small buffer, Utf8Json/Spanjson will rent a new buffer from the pool and copy the data and continue.

TORNHOOF (@TORN_HOOF)

As the payload in Test 2 is quit big (36,6 KB), it might explain the increase in throughput compared to test 1.

Categories
.NET

Entity Framework Core 3

I recently upgraded an .NET Core 2 project to .NET Core 3 and with that I also updated other packages, including EF Core. Now EF Core 3 has quite a list of breaking changes you can se them in the list below.

https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaking-changes

The worst change for my application was one that is quit a bit down on the list and one that I did not see until I found the actual issue in my code.

Before 3.0, eagerly loading collection navigations via Include operators caused multiple queries to be generated on relational database, one for each related entity type.

Eager loading of related entities now happens in a single query

This does not sound to bad, nothing will stop working, but it will effect performance. If it effects performance depends on your query, some will go faster and some will go slower.

In my case I had an query that did not like this change at all. the query created an list of items. These items then had a bunch of data from other tables, created an 3d dimensional object. I Mocked up an example below on how the Linq Query looked like.

return context.Item
	.Select(i => new ResultItem()
	{
		ItemId   = i.ItemId,
		Siblings = i.parentNavigation.Items.Count(),
		Position = i.parentNavigation.Items.Count(p => p.Order < I.Order),
		tags = i.Tag.Select(t => new ResultTag {
			tag = t.tag
		})
	}).ToList();

In EF Core 2, this worked quite well. EF Core divided the code in two parts. First it did one query to fetch the items, count the siblings and position, and then it would do multiple queries to fetch the tags. So if I fetched 12 items. it would be 1 query to fetch the items and then 12 queries to fetch all the tags.

EF Core 3 however does one query, ONE BIG QUERY. In this case it resulted in some performance drop. In EF Core 2 it had a stable 250ms execution time, independent on how many tags each item had. In EF Core 3 it hovered around 650ms but could be slower if one item had more tags than usual.

Improvement 1

var items = context.Item
	.Select(i => new ResultItem()
	{
		ItemId   = i.ItemId,
                ParentId = i.ParentId,
                Order = i.Order
	}).ToList();

foreach (var item in items)
{
	item.Siblings = context.Item.Count(i => i.ParentId == item.ParentId);
	item.Position = context.Item.Count(i => i.ParentId == item.ParentId && i.Order < item.Order);
	item.tags = context.Tag.Where(t => t.ItemId = item.ItemId).Select(t => new ResultTag {
			tag = t.tag
		}).ToList();
}

First step was to separate the different queries. We simply fetched the item first and then looped over them to fetch the extra data needed. This resulted in quite some improvement, we decreased the time from around 650ms to 350ms. But it was still not fast enough.

The reason it still not as fast as before is because this code results in a lot of queries. If we fetch 12 items, it will result 1 query to fetch the items, then it will loop over all the items and do 3 separate queries for each item. Resulting in a total of 1+36 queries against the database.

Each query means another roundtrip to the database. And this is partly the reason why EF Core decided to make the change in EF core 3, to try to avoid as many roundtrips as possible.

Improvement 2

var items = context.Item
	.Select(i => new ResultItem()
	{
		ItemId   = i.ItemId,
                ParentId = i.ParentId,
                Order = i.Order
	}).ToList();

var itemIds = items.Select(c => c.ItemId);
var tags = context.Tag.Where(t => itemIds.Contains(t.ItemId)).Select(t => new ResultTag {
	ItemId = t.ItemId,
	Tag = t.Tag
}).ToList();

var parentIds = items.Select(i => i.ParentId);
var siblings = context.Item.Where(i => parentIds.Contains(i.parentId)).Select(i => new SiblingItem {
	ParentId = i.ParentId,
	Order = i.Order
}).ToList();

foreach (var item in items)
{
	item.Siblings = siblings.Count(i => i.ParentId == item.ParentItem);
	item.Position = siblings.Count(i => i.ParentId == item.ParentItem && i.Order < item.Order);
	item.tags = tags.Where(t => t.ItemId = item.ItemId);
}

Now we have complicated our code somewhat, but this was the fastest way of doing it. As previous example we first fetch all the items. Our next step is for all items we fetched, select their siblings in on query and select all their tags in another query. We can then loop over the items and filter out the result for each item. This resulted in a total of 3 queries being sent to the database and the execution time dropped to under 100ms.

Categories
Programming

VS Code: Wrap Attributes

Go to File > Settings > Preferences. Expand Extensions and click on HTML. Scroll down until you find the setting Wrap Attribute. set to force-aligned

Enabling this setting will make the formatter to but each html attributes on a new line. You activate the formatter by pressing Shift+Alt+ F

Categories
Angular

Upgrading Angular

Run this to globally upgrade your angular version to latest.

ng --version
npm uninstall -g angular-cli 
npm cache verify
npm install -g @angular/cli@latest
Categories
Programming

Deploy keys Github

Deploy keys gives you read access by default to a single repository. Deploy keys can be given write access when created in github.

In CentOS 7.

yum install rh-git29-git -y

Get public key to import to github.

cat ~/.ssh/id_rsa.pub

If you dont have a private/public key create one witht he following command

ssh-keygen -t rsa -b 4096 -C "you@email.com"

To test validation

ssh -T git@github.com
Categories
Programming

Crontab Directories

Running the command crontab -e creates a user defined cron. this file (edited with vim) is stored in /var/spool/cron/. This file does not have a user column

Creating files in /etc/cron.d is not per user. and these files does have a user column.

Categories
Programming

Upgrade of Galera Cluster from 10.1 to 10.2

This is how i did a rolling upgrade of an MariaDB Galera Cluster from version 10.1 to 10.2 in CentOS 7. Reference Upgrading Galera Cluster.

Categories
Programming

Test if files exists

Searches in $directory for any files that begin with $today. If it find any files the script will exit.

[code language=”bash”]
#!/bin/bash

if [ $daily -eq "1" ] ; then
if ! find $directory -type f -name "$today*" -exec false {} + ; then
exit;
fi;
fi;
[/code]

Categories
Programming

Test if pingable

The code snippet belows try to ping $host. if ping fails (ping returns none zero value exitcode) the script exits.

[code language=”bash”]
#!/bin/bash
if [ $ping -eq "1" ] ; then
if ! ping -w 4 -c 1 $host > /dev/null 2>&1 ; then
exit
fi;
fi;
[/code]