{"id":11973,"date":"2020-09-30T16:57:46","date_gmt":"2020-09-30T14:57:46","guid":{"rendered":"https:\/\/blog.mi.hdm-stuttgart.de\/?p=11973"},"modified":"2023-06-18T18:09:36","modified_gmt":"2023-06-18T16:09:36","slug":"ts3-voice-channel-manager","status":"publish","type":"post","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2020\/09\/30\/ts3-voice-channel-manager\/","title":{"rendered":"TS3 Voice Channel Manager &#8211; Create and push a Bot to its Limits"},"content":{"rendered":"\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>by Jan Kaupe (jk206)<\/p><\/blockquote>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"905\" height=\"402\" data-attachment-id=\"11975\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2020\/09\/30\/ts3-voice-channel-manager\/webapp\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/webapp.png\" data-orig-size=\"905,402\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"webapp\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/webapp.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/webapp.png\" alt=\"\" class=\"wp-image-11975\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/webapp.png 905w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/webapp-300x133.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/webapp-768x341.png 768w\" sizes=\"auto, (max-width: 905px) 100vw, 905px\" \/><figcaption>Figure 1: Web Configuration Panel for the Bot<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p>TeamSpeak\u00b3 is a Voice-over-IP application allowing users to connect to a server where they can join Voice Channels to communicate with each other.<\/p>\n\n\n\n<p>Anyone can download and host own TS\u00b3 servers. Huge community servers have been established. However, these servers usually have way more Voice Channels than users, resulting in a decreased user experience.<\/p>\n\n\n\n<p>To resolve this issue, I created a proof-of-concept Bot which is able to create and delete Voice Channels on demand. This Bot can be managed via a Web Configuration Panel.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"495\" height=\"586\" data-attachment-id=\"11983\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2020\/09\/30\/ts3-voice-channel-manager\/showcase\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/showcase.gif\" data-orig-size=\"495,586\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"showcase\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/showcase.gif\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/showcase.gif\" alt=\"\" class=\"wp-image-11983\"\/><figcaption>Figure 2: TS3 Voice Channel Manager Showcase<\/figcaption><\/figure>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Used Technologies<\/h2>\n\n\n\n<p>The Bot is written in <strong>C# (ASP .NET Core 3.1)<\/strong> and uses these awesome libraries:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/github.com\/jbogard\/MediatR\" target=\"_blank\" rel=\"noopener noreferrer\">jbogard \/ MediatR<\/a> &#8211; Loose Coupling via Requests and Notifications<\/li><li><a href=\"https:\/\/github.com\/dotnet\/efcore\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/github.com\/dotnet\/efcore\">dotnet \/ efcore<\/a> &#8211; Object Database Mapper for .NET<\/li><li><a href=\"https:\/\/github.com\/nikeee\/TeamSpeak3QueryAPI\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/github.com\/nikeee\/TeamSpeak3QueryAPI\">nikeee \/ TeamSpeak3QueryAPI<\/a> &#8211; API wrapper for TS\u00b3 Query<\/li><li><a href=\"https:\/\/github.com\/JamesNK\/Newtonsoft.Json\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/github.com\/JamesNK\/Newtonsoft.Json\">JamesNK \/ Newtonsoft.Json<\/a> &#8211; JSON framework for .NET<\/li><li><a href=\"https:\/\/github.com\/NLog\/NLog\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/github.com\/NLog\/NLog\">NLog<\/a> &#8211; Logging platform for .NET<\/li><\/ul>\n\n\n\n<p>The Bot is being tested using the following tools\/libraries:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/www.docker.com\/\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/www.docker.com\/\">Docker<\/a> &#8211; OS-Level virtualization which allows to create Containers<\/li><li><a href=\"https:\/\/github.com\/dotnet\/Docker.DotNet\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/github.com\/dotnet\/Docker.DotNet\">dotnet \/ Docker.DotNet<\/a> &#8211; Simplifies interaction with Docker Demons<\/li><li><a href=\"https:\/\/github.com\/FluentValidation\/FluentValidation\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/github.com\/FluentValidation\/FluentValidation\">FluentValidation<\/a> &#8211; Fluent validation library for .NET<\/li><li><a href=\"https:\/\/github.com\/xunit\/xunit\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/github.com\/xunit\/xunit\">xUnit.net<\/a> &#8211; Unit Testing Framework<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Goals and Challenges<\/h2>\n\n\n\n<p>I&#8217;ve set three goals for myself and faced some time consuming challenges while realizing them because my expectations did not always match the reality.<\/p>\n\n\n\n<p> I&#8217;ll give you a brief overview about those, before I explain them in detail:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Goal: <\/strong>Managing multiple servers.<\/li><li><strong>Expectation:<\/strong> I simply use a Thread for each server, right?<\/li><li><strong>Reality:<\/strong> Race Conditions and Exceptions. Exceptions everywhere.<\/li><\/ul>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Goal:<\/strong> Write real Integration Tests.<\/li><li><strong>Expectation:<\/strong> I can just spawn Docker Containers and use them, right?<\/li><li><strong>Reality:<\/strong> Container is not ready and won&#8217;t react to requests.<\/li><\/ul>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Goal:<\/strong> Figure out the limits of the Bot.<\/li><li><strong>Expectation:<\/strong> Add servers until Bot crashes.<\/li><li><strong>Reality:<\/strong> Crashes. Fix thread-safety issues. Repeat. =&gt; Now never crashes?<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Managing multiple Servers<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">Starting with a single Server<\/h4>\n\n\n\n<p>Before I could be able to manage multiple servers, I decided to manage a single server first.<\/p>\n\n\n\n<p>So I started to study the ServerQuery documentation which is part of the <a href=\"https:\/\/teamspeak.com\/en\/downloads\/#server\" title=\"https:\/\/teamspeak.com\/en\/downloads\/#server\" target=\"_blank\" rel=\"noreferrer noopener\">TeamSpeak\u00b3 Server Download<\/a>. It explains how to connect to the Query via Telnet and interact with the Query. <\/p>\n\n\n\n<p>Next I tried some experiments with the library <a href=\"https:\/\/github.com\/nikeee\/TeamSpeak3QueryAPI\" target=\"_blank\" rel=\"noreferrer noopener\">nikeee \/ TeamSpeak3QueryAPI<\/a>. Soon, I was able to manage a single server as shown in <strong>Figure 2<\/strong>.<\/p>\n\n\n\n<div style=\"padding: 20px;background-color: #AAA;color: white;margin-bottom: 15px;\">\n  NOTE:<br>\nI realized that the library was not thread-safe so I had to clone and change it to prevent race conditions.\n<\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Refactoring required!<\/h4>\n\n\n\n<p>I first split my code into multiple methods and some classes. Then I tried to wrap all the code so I can start a thread for each server. Then I realized, that I just created code which was not maintainable nor testable.<\/p>\n\n\n\n<p><strong>Why?<\/strong><br>Simply splitting code into methods and classes does not remove dependencies!<\/p>\n\n\n\n<p>How to reduce Dependencies?<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Onion Architecture to the Rescue!<\/h4>\n\n\n\n<p>I found <a href=\"https:\/\/jeffreypalermo.com\/2008\/07\/the-onion-architecture-part-1\/\" title=\"https:\/\/jeffreypalermo.com\/2008\/07\/the-onion-architecture-part-1\/\">this article &#8211; The Onion Architecture<\/a> by Jeffrey Palermo. It explains how to establish multiple layers within your application and how to reduce dependencies by lose coupling.<\/p>\n\n\n\n<p>So I started to split my code base into multiple projects:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"860\" height=\"747\" data-attachment-id=\"12025\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2020\/09\/30\/ts3-voice-channel-manager\/project\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/project.png\" data-orig-size=\"860,747\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"project\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/project.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/project.png\" alt=\"\" class=\"wp-image-12025\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/project.png 860w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/project-300x261.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/project-768x667.png 768w\" sizes=\"auto, (max-width: 860px) 100vw, 860px\" \/><figcaption>Figure 3: Solution Explorer &#8211; One project split into seven projects<\/figcaption><\/figure>\n\n\n\n<p>The following lower projects are allowed to reference upper projects but not vice versa:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Domain<\/strong> &#8211; Contains application data classes (e.g. Channels)<\/li><li><strong>Abstractions<\/strong> &#8211; Contains Interfaces, Notification and Exception classes<\/li><li><strong>Service<\/strong> &#8211; Business logic (how to manage Channels)<\/li><li><strong>Infrastructure<\/strong> &#8211; How to access database (repository classes) and TS\u00b3 servers with basic operations<\/li><li><strong>Web<\/strong> &#8211; Frontend Code which mostly uses Service classes<\/li><\/ul>\n\n\n\n<p>After splitting the code into multiple layers I realized that this architecture helped me drastically to simplify writing Unit Tests:<\/p>\n\n\n\n<!-- Unit Test -->\n<script src=\"https:\/\/gist.github.com\/Morodar\/e49567379ca82b6bcbb05643af5db4f8.js\"><\/script>\n\n\n\n<p>The code above shows a test which stores a <strong>ServerConfig<\/strong> in an <strong>InMemoryDatabase<\/strong> using a <strong>ServerConfigRepository<\/strong>.<\/p>\n\n\n\n<p><strong>ServerConfigRepository<\/strong> does not know what to do with a <strong>ServerConfig<\/strong> but it can store it and retrieves it for you. Nothing more, nothing less.<\/p>\n\n\n\n<p>I also realized that I could spot classes easier which need locking so that they can be thread-safe (usually Repositories were affected).<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Why Threads? Use Async\/Await!<\/h4>\n\n\n\n<p>Next, I figured out that the TS\u00b3 API uses infinite loops inside Tasks which can be canceled by a <strong>CancellationTokenSource<\/strong>. Inside the infinite loop, a <strong>TcpClient <\/strong>is used which listens for incoming messages by awaiting the next sent line. <\/p>\n\n\n\n<p>Here a simplified example of the loop:<\/p>\n\n\n\n<!-- QueryClient Loop -->\n<script src=\"https:\/\/gist.github.com\/Morodar\/073fb80f981177a38b4c8b82abefcd01.js\"><\/script>\n\n\n\n<p>Thanks to async\/await I can write asynchronous code which looks synchronous &#8211; Something I feel much more comfortable.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">But awaiting an infinite Task does not make sense!<\/h4>\n\n\n\n<p>That&#8217;s right. So I don&#8217;t await it  (see line 23 in the following code snippet). But I keep a reference to the object holding the <strong>CancellationTokenSource<\/strong> so I can stop it when I want to!<\/p>\n\n\n\n<p>This is managed by the <strong>TsClientRepository<\/strong>:<\/p>\n\n\n\n<!-- TsClientRepository -->\n<script src=\"https:\/\/gist.github.com\/Morodar\/8ba89159a1255acfa979dcf6ab99c992.js\"><\/script>\n\n\n\n<p>The only thing left to do is to fill the repository with <strong>ServerConfigs<\/strong>. This is done when the Application is started:<br>(or a <strong>ServerConfig<\/strong> is added)<\/p>\n\n\n\n<!-- Application started -->\n<script src=\"https:\/\/gist.github.com\/Morodar\/75eb20ac9e41df0b253a2cbc87ddfb03.js\"><\/script>\n\n\n\n<p>The Code above also demonstrates the <strong>Mediator-Pattern<\/strong>. It leverages louse-coupling. Some classes <strong>notice something<\/strong> and raise events. <strong>Other <\/strong>classes <strong>handle <\/strong>the raised events. A mediator <strong>decouples <\/strong>them. Thus the <strong>only dependency<\/strong> between them is the <strong>Mediator<\/strong>! <\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Writing real Integration Tests<\/h2>\n\n\n\n<p>When it comes to testing, we may use mocks as helpers. Mocking allows testing our Code without real connections\/databases\/files etc. But I didn&#8217;t want to mock a TS\u00b3 Server to test my Bot!<\/p>\n\n\n\n<p>So I decided to use <a href=\"https:\/\/www.docker.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Docker<\/a> and <a href=\"https:\/\/github.com\/dotnet\/Docker.DotNet\" target=\"_blank\" rel=\"noreferrer noopener\">Docker.DotNet<\/a> to create TS\u00b3 containers.<br>But I quickly realized that a lot of Code is required to create such container.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Fixtures to the rescue!<\/h3>\n\n\n\n<p>I want to write easy to create and simple tests. Using a custom fixture class which sets everything up for you is key.<\/p>\n\n\n\n<p>In my case, the fixture has to do the following things <br>(after a lot of trial and error):<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Create DockerClient<\/li><li>Check\/Download TS\u00b3 Image (please use version tags!)<\/li><li>Find available TCP Ports (so that the Bot can connect to it)<\/li><li>Create the Container and mount available TCP Port<\/li><li>Start the Container<\/li><li>Finally, wait for the TS\u00b3 Server inside the container to spin up<\/li><\/ul>\n\n\n\n<p>Last one was tough. I created a helper class which polls the TS\u00b3 Server and expects a <strong>Welcome Message<\/strong>. If I don&#8217;t receive the welcome message within certain attempts, the test fails (since I then expect the setup to be wrong).<\/p>\n\n\n\n<!-- Tcp Helper -->\n<script src=\"https:\/\/gist.github.com\/Morodar\/d7f6de84e0057f84dd538af681481f70.js\"><\/script>\n\n\n\n<p>And when I receive the welcome message, the TS\u00b3 is ready to act as our test resource! <br>How to use it (line 5, 6 and 19):<\/p>\n\n\n\n<!-- TestExample -->\n<script src=\"https:\/\/gist.github.com\/Morodar\/39602961e0db60069e753454c34af382.js\"><\/script>\n\n\n\n<p>Simple, right?<br>Thanks to <strong>using<\/strong>, the inner block is wrapped by a <strong>try\/catch<\/strong>\/<strong>finally<\/strong> which disposes the fixture. If the test fails, the container is guaranteed to be cleaned up and deleted!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Figure out the limits of the Bot<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">The Plan<\/h3>\n\n\n\n<p>Start, stop and delete hundreds of TS\u00b3 servers for each test on my machine using docker-compose. <br>I wrote a config generator to simplify that task. It generated the required docker-compose files and suited JSON files to later add them to my Bot via the Web Application.<\/p>\n\n\n\n<p>When everything is setup, I started the Bot and the following should happen:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Read <strong>ServerConfigs <\/strong>from database<\/li><li>Create a <strong>TS3Client<\/strong> for each configuration<\/li><li>Connect to the server and subscribe for changes<\/li><li>&#8220;Manage&#8221; the <strong>ChannelsToManage<\/strong> which will result in creation of at least two channels in this scenario<\/li><li>Keep the connection alive and wait for changes<\/li><\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">What will be measured?<\/h4>\n\n\n\n<ul class=\"wp-block-list\"><li>Server configuration count ( = Connections the Bot will establish)<\/li><li>Startup time (includes connecting to servers and channel creation)<\/li><li>Response time <strong>after <\/strong>startup <br>(how long does it take to create additional channels?)<\/li><li>RAM usage <strong>after <\/strong>startup<\/li><\/ul>\n\n\n\n<p>Since I&#8217;ve added verbose logging, I could trace how long the Bot needs to fully spin up and how fast it reacts to changes on TS\u00b3 servers.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">What problems did I encounter?<\/h4>\n\n\n\n<p>While testing, I had to change my code multiple times because of:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Docker-Compose doesn&#8217;t like to start 500 containers at once<br>(split to 100 container configs and start each 100 manually)<\/li><li>Race conditions &#8211; Writing thread-safe code is not easy \ud83d\ude05<\/li><li>Race conditions AGAIN &#8211; The <a href=\"https:\/\/github.com\/nikeee\/TeamSpeak3QueryAPI\" target=\"_blank\" rel=\"noreferrer noopener\">TS\u00b3QueryAPI<\/a> was not thread-safe too (<a href=\"https:\/\/github.com\/nikeee\/TeamSpeak3QueryAPI\/issues\/52\" title=\"https:\/\/github.com\/nikeee\/TeamSpeak3QueryAPI\/issues\/52\" target=\"_blank\" rel=\"noreferrer noopener\">Issue<\/a>)<\/li><li>I could &#8220;only&#8221; run 500 TS\u00b3 servers. The Bot could handle them without any issues (after previous problems have been resolved). So I decided to let the Bot connect to the server multiple times managing different channels. (Unrealistic scenario, but works for benchmarking purposes since more connections = more stress for the Bot)<\/li><li>Then TS\u00b3 servers refused connections because of too many simultaneous connections at once. (only 5 query connections per IP are allowed which is not documented)<\/li><li>Too much console logging and some other issues did slow down the bot drastically. After fixing them, the Bot got banned AGAIN because it was too fast. It triggered the <strong><a href=\"https:\/\/forum.teamspeak.com\/threads\/136423-Server-gt-3-3-0-The-New-Query-System\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/forum.teamspeak.com\/threads\/136423-Server-gt-3-3-0-The-New-Query-System\">&#8220;new query flooding system&#8221;<\/a><\/strong>.<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">How did Testing look like?<\/h3>\n\n\n\n<p>I uploaded a video demonstrating a benchmark test.<br>I spin up 500 TS\u00b3 Servers using docker-compose on my workstation. Then I used SSH to connect to a Debian LXC Container which will run the Bot. The LXC container had two cores running at 2GHz and 1 GB of RAM assigned. <\/p>\n\n\n\n<iframe loading=\"lazy\" src=\"https:\/\/www.youtube-nocookie.com\/embed\/bGrKGr3QIac\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen=\"\" width=\"560\" height=\"315\" frameborder=\"0\"><\/iframe>\n\n\n\n<p>The video illustrates how the Bot needs a lot CPU resources during startup. When switching channels, the reactions where delayed at the beginning. But the delay vanishes after the initialization procedure and the CPU is barely used.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The results<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">Not optimized<\/h4>\n\n\n\n<p>The table shows that the startup time takes much longer the more servers need to be managed. But when the Bot finished initialization response time seem to be pretty constant.<\/p>\n\n\n\n<p>The <strong>channels per s<\/strong> shows the true limit of the servers. If user activity is higher than the channels per s, the server won&#8217;t be able to keep up!<br>(at least those values help to get a feeling about the possible capacity)<\/p>\n\n\n\n<p>So I looked for reasons why the startup\/managing part took so much time.  Logging was the issue!<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>Connections<\/td><td>Channels to create<\/td><td>Startup time<\/td><td>Channels per s<\/td><td>Response time<\/td><td>RAM Usage<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>1s<\/td><td>&#8211;<\/td><td>&#8211;<\/td><td>44 MB<\/td><\/tr><tr><td>500<\/td><td>1000<\/td><td>21s<\/td><td>47.6<\/td><td>125ms<\/td><td>267 MB<\/td><\/tr><tr><td>1000<\/td><td>2000<\/td><td>47s<\/td><td>42.6<\/td><td>120ms<\/td><td>265 MB<\/td><\/tr><tr><td>1500<\/td><td>3000<\/td><td>68s<\/td><td>44.1<\/td><td>122ms<\/td><td>299 MB<\/td><\/tr><tr><td>2000<\/td><td>4000<\/td><td>89s<\/td><td>44.9<\/td><td>130ms<\/td><td>309 MB<\/td><\/tr><tr><td>2500<\/td><td>5000<\/td><td>103s<\/td><td>24.3<\/td><td>127ms<\/td><td>360 MB<\/td><\/tr><\/tbody><\/table><figcaption>Table 1: Not optimized results on LXC container<\/figcaption><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Somewhat optimized<\/h4>\n\n\n\n<p>After enabling lazy logging (caches, bulk writes log entries and keep the file open) and reducing console logging I achieved much better results!<\/p>\n\n\n\n<p>Apparently, by having 5 connections to a TS\u00b3 server I trigger the <strong><a href=\"https:\/\/forum.teamspeak.com\/threads\/136423-Server-gt-3-3-0-The-New-Query-System\" target=\"_blank\" rel=\"noreferrer noopener\">&#8220;new query flooding system&#8221;<\/a><\/strong> too easily. The Bot has not been designed to establish multiple connections to a single server.<\/p>\n\n\n\n<p>To prevent the flooding system, I delayed the startup by creating TS3Cliens slowly (100 at once and then wait short time before I add the next 100).<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>Connections<\/td><td>Channels to create<\/td><td>Startup time<\/td><td>Channels per s<\/td><td>Response time<\/td><td>RAM Usage<\/td><\/tr><tr><td>0<\/td><td>0<\/td><td>1s<\/td><td>&#8211;<\/td><td>&#8211;<\/td><td>47 MB<\/td><\/tr><tr><td>500<\/td><td>1000<\/td><td>2.5s<\/td><td>400<\/td><td>166ms<\/td><td>225 MB<\/td><\/tr><tr><td>1000<\/td><td>2000<\/td><td>13s<\/td><td>153.8<\/td><td>181ms<\/td><td>237 MB<\/td><\/tr><tr><td>1500<\/td><td>3000<\/td><td>9s<\/td><td>333.0<\/td><td>192ms<\/td><td>282 MB<\/td><\/tr><tr><td>2000<\/td><td>4000<\/td><td>14s<\/td><td>285.7<\/td><td>160ms<\/td><td>319 MB<\/td><\/tr><tr><td>2500<\/td><td>5000<\/td><td>20s<\/td><td>250<\/td><td>194ms<\/td><td>345 MB<\/td><\/tr><\/tbody><\/table><figcaption>Table 2: Somewhat optimized results on LXC container<\/figcaption><\/figure>\n\n\n\n<p>Response time seemed to increase because of lazy logging. So logging time spans seem to be inaccurate.<\/p>\n\n\n\n<p>Due to slow TS3Client creation (which would not be required in &#8220;one connection per TS\u00b3 server&#8221;), Startup Times are higher than in reality.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Thanks to this project I could learn a lot about how to:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Split the code into multiple Abstraction Layers<br>(But you should also not over engineer it)<\/li><li>Use the Mediator Pattern<br>(But you have to know how the communication flows works)<\/li><li>Use async \/ await and work with long running Tasks<\/li><li>Find classes that need to be thread-safe faster<\/li><li>Write classes thread-safe<\/li><li>Write Integration Tests with Docker<\/li><\/ul>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>by Jan Kaupe (jk206) Introduction TeamSpeak\u00b3 is a Voice-over-IP application allowing users to connect to a server where they can join Voice Channels to communicate with each other. Anyone can download and host own TS\u00b3 servers. Huge community servers have been established. However, these servers usually have way more Voice Channels than users, resulting in [&hellip;]<\/p>\n","protected":false},"author":994,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[120,650],"tags":[],"ppma_author":[830],"class_list":["post-11973","post","type-post","status-publish","format-standard","hentry","category-cloud-technologies","category-scalable-systems"],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":1393,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2016\/09\/05\/botnets-structural-analysis-functional-principle-and-general-overview\/","url_meta":{"origin":11973,"position":0},"title":"Botnets &#8211; Structural analysis, functional principle and general overview","author":"Michael Kreuzer","date":"5. September 2016","format":false,"excerpt":"This paper provides an overview on the most important types of botnets in terms of network topology, functional principle as well as a short definition on the subject matter. By exploring the motivation of botnet operators, the reader will gain more insight into business models and course of actions of\u2026","rel":"","context":"In &quot;Secure Systems&quot;","block_context":{"text":"Secure Systems","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/system-designs\/secure-systems\/"},"img":{"alt_text":"wiat wektor","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2016\/09\/Fotolia_67526425_M-300x169.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2016\/09\/Fotolia_67526425_M-300x169.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2016\/09\/Fotolia_67526425_M-300x169.jpg?resize=525%2C300&ssl=1 1.5x"},"classes":[]},{"id":20964,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/09\/15\/enterprise-social-audio-research-implementation-and-opportunities\/","url_meta":{"origin":11973,"position":1},"title":"Enterprise Social Audio &#8211; Research, Implementation and Opportunities","author":"Mario Koch","date":"15. September 2021","format":false,"excerpt":"Enterprise Socia Audio - does it work, what are non-negotiables, what are the challenges? Research and implementation in close collaboration with clients.","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/09\/jason-rosewell-ASKeuOZqhYU-unsplash-1-edited-scaled.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/09\/jason-rosewell-ASKeuOZqhYU-unsplash-1-edited-scaled.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/09\/jason-rosewell-ASKeuOZqhYU-unsplash-1-edited-scaled.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/09\/jason-rosewell-ASKeuOZqhYU-unsplash-1-edited-scaled.jpg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/09\/jason-rosewell-ASKeuOZqhYU-unsplash-1-edited-scaled.jpg?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/09\/jason-rosewell-ASKeuOZqhYU-unsplash-1-edited-scaled.jpg?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":22530,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/28\/discord-monitoring-system-with-amplify-and-ec2\/","url_meta":{"origin":11973,"position":2},"title":"Discord Monitoring System with Amplify and EC2","author":"mk322","date":"28. February 2022","format":false,"excerpt":"Abstract Discord was once just a tool for gamers to communicate and socialize with each other, but since the pandemic started, discord gained a lot of popularity and is now used by so many other people, me included, who don't necessarily have any interest in video gaming. So after exploring\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/data-workflow.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/data-workflow.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/data-workflow.png?resize=525%2C300&ssl=1 1.5x"},"classes":[]},{"id":3864,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2018\/08\/07\/server-less-computing-vs-security\/","url_meta":{"origin":11973,"position":3},"title":"Server \u201cless\u201d Computing vs. Security","author":"Merve Uzun","date":"7. August 2018","format":false,"excerpt":"Summary about Serverless Computing with Security aspects.","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2018\/08\/Funktionsweise-300x98.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":8794,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2019\/09\/09\/the-insecurity-about-speaker-legitimacy-detection\/","url_meta":{"origin":11973,"position":4},"title":"The (in)security about speaker legitimacy detection","author":"Remo Schneider","date":"9. September 2019","format":false,"excerpt":"For the most of us, voices are a crucial part in our every-day communication. Whether we talk to other people over the phone or in real life, through different voices we\u2019re able to distinguish our counterparts, convey different meanings with the same words, and \u2013 maybe most importantly \u2013 connect\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/09\/areas1.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/09\/areas1.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/09\/areas1.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/09\/areas1.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":24243,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2023\/03\/03\/modern-application-of-voice-ai-technology\/","url_meta":{"origin":11973,"position":5},"title":"Modern application of Voice AI technology","author":"Ngoc Ton","date":"3. March 2023","format":false,"excerpt":"With the advancement of technology and the gradually increasing use of artificial intelligence, new markets are developed. One of such is the market of Voice AI which became a commercial success with voice bots such as Alexa or Siri. They were mainly used as digital assistants who could answer questions,\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/03\/01.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/03\/01.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/03\/01.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/03\/01.jpg?resize=700%2C400&ssl=1 2x"},"classes":[]}],"jetpack_sharing_enabled":true,"authors":[{"term_id":830,"user_id":994,"is_guest":0,"slug":"jk206","display_name":"jk206","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/10302563a549c3d34d5ada028bc6043aa17d60c20ab8fed95921bf73fc6ead2e?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/11973","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/users\/994"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/comments?post=11973"}],"version-history":[{"count":58,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/11973\/revisions"}],"predecessor-version":[{"id":12132,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/11973\/revisions\/12132"}],"wp:attachment":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/media?parent=11973"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/categories?post=11973"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/tags?post=11973"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/ppma_author?post=11973"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}