<?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: Eva Marija Banaj Gađa</title>
    <description>The latest articles on DEV Community by Eva Marija Banaj Gađa (@netcat5679).</description>
    <link>https://dev.to/netcat5679</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%2F651035%2F2856139a-1fb8-469a-857e-2cafa9960d36.jpg</url>
      <title>DEV Community: Eva Marija Banaj Gađa</title>
      <link>https://dev.to/netcat5679</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/netcat5679"/>
    <language>en</language>
    <item>
      <title>It is just work</title>
      <dc:creator>Eva Marija Banaj Gađa</dc:creator>
      <pubDate>Tue, 26 Mar 2024 15:42:40 +0000</pubDate>
      <link>https://dev.to/netcat5679/it-is-just-work-3k0k</link>
      <guid>https://dev.to/netcat5679/it-is-just-work-3k0k</guid>
      <description>&lt;p&gt;There isn't a magic solution to burnout. We all have different aspirations, goals, motivations and sources of stress. &lt;br&gt;
This is a short tale of how I changed my perspective, and by doing that, managed to handle my burnout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What do we get from our jobs, apart from the obvious? 💸&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A sense of accomplishment and worth. All of us are, to some degree, defined by our work. How can we not be? We have a need to prove ourselves, to progress our career, to be the best at what we do, to be recognized and valued for our contributions. More often than not, we spend more time at work than we do with our families.&lt;/p&gt;

&lt;p&gt;This is a slippery slope if you ask me, because, what if the job we do requires more from us than we are capable of giving? Do we just accept that? Do we decide that things will get better in time and it was just a temporary setback due to some underlying cause that might not even be related to our capabilities? Do we tell ourselves we are doing the best we can and everything will work out in the end?&lt;/p&gt;

&lt;p&gt;I don't. At least I didn't. &lt;/p&gt;

&lt;p&gt;Instead, I felt like a failure. But, fear not! The solution is simple. Just put in some extra hours, get more things done by bringing your problems home and it will get better and your effort will be recognized. Right?&lt;/p&gt;

&lt;p&gt;Once I reached the point of feeling like I'm losing ground beneath my feet, no amount of extra hours made me feel less like and impostor that should not be here and will be found out at any second.&lt;/p&gt;

&lt;p&gt;And then I realized. I was never found out. That means that either I'm the best impostor that ever was, or that I hold myself to an impossible standard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🌟 Little milestones&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I started my first job as a software engineer while I was still at college. Everything was a challenge, and an interesting puzzle I had to solve. As fun as it was to play detective, I noticed that it didn't stop when I went home. Some background process was always running. It became tiring. Colleagues assured me that it is "normal" and "natural", because that "is the job". And I believed them for a while.&lt;/p&gt;

&lt;p&gt;But why should it be "normal" or "natural". I never signed up to spend all my time working, actively or passively. So, my first attempt to avoid this was to stay at work until I finish whatever it was that I started. This resulted in quite a bit of overtime, but it did the trick. Then it evolved into not even starting anything that I know I cannot finish before the end of the day. And that is something I still do to this day - but I built on it a little.&lt;/p&gt;

&lt;p&gt;Instead of thinking of each ticket as a whole, I'm thinking of each ticket as a little project on its own. This way, even though the ticket is not yet done, 3 out of 4 whole milestones are done, it is 5 pm and I can close my laptop and not think about it until tomorrow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💚 Take care of yourself&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Listen to yourself. If you need a break - take it. If you are sick, go on a sick leave and get back to 100%. And I mean 100%, not just barely good enough to survive. &lt;br&gt;
It is not illegal to go on sick leave, even though it might feel like it sometimes. Your colleagues will manage for a couple of days until you get that well deserved respite your body requires.&lt;br&gt;
Same thing goes for holidays. Always have a holiday booked - have something to look forward to. It is important for ones productivity and well-being to regularly take time to rest and get away from everything.&lt;br&gt;
World will not come to a halt if you are unable to can anymore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❗Do your best, but don't give your all&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And I can't stress this enough. Whenever I started a new job, I always had this need to prove myself, to show how much I can do. And that is amazing as long as you can keep delivering that rate all day, every day.&lt;/p&gt;

&lt;p&gt;At first sign something is off, and your output falls, everyone is going to be super surprised and will expect of you to get back to "your old self" as soon as possible. This usually stressed me further causing my output to fall even lower until I had to take a couple of weeks off to recuperate.&lt;/p&gt;

&lt;p&gt;So I decided that was not optimal or sustainable. I gave a little bit of effort into finding a pace that works for everyone. A pace that will not burn me out, that I can keep up consistently and that provides output at sufficient intervals. &lt;/p&gt;

&lt;p&gt;Then, when hypothetical s*it hits the fan, it is not such a problem to pull my weight and then some for a couple of weeks, and then lull back into my comfortable, productive pace that is not taking a toll on my health.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🕔 It is just work&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Simple as that. Today it is this company, tomorrow it will be that company. We are essentially selling our time and skill to the best of our abilities to provide ourselves with means to do what we enjoy in our free time.&lt;/p&gt;

&lt;p&gt;Best things in our life should be those that happen in the time we didn't sell. If we are not enjoying the time that is ours, if we are not spending it in a fulfilling way with people we love, then what are working for?&lt;/p&gt;

&lt;p&gt;At the end of the day, it indeed is just work.&lt;/p&gt;

</description>
      <category>burnout</category>
      <category>learning</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Fun times with MySQL upgrade</title>
      <dc:creator>Eva Marija Banaj Gađa</dc:creator>
      <pubDate>Fri, 17 Dec 2021 09:25:00 +0000</pubDate>
      <link>https://dev.to/trikoder/fun-times-with-mysql-upgrade-1ei4</link>
      <guid>https://dev.to/trikoder/fun-times-with-mysql-upgrade-1ei4</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AhjC30NH5V2DHVq-POKlMpg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AhjC30NH5V2DHVq-POKlMpg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like all service upgrades, MySQL is no different. Bump the service version, build the docker image, try to “make up“ the project and hope for the best. I’ve decided to dedicate this blog to four things that turned this “it is going to be an easy project” to “8 months in hell while upgrading MySQL from v5.6 to v8.0”.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Fantastic SQL modes and where to find them
&lt;/h3&gt;

&lt;p&gt;While looking at all changes made to MySQL between v5.6 and v8.0, I came across something rather interesting. They announced they had enabled a bunch of other previously optional SQL modes as a part of strict mode.&lt;/p&gt;

&lt;p&gt;To my surprise, only &lt;em&gt;NO_ENGINE_SUBSTITUTION&lt;/em&gt; mode was enabled in our database. 😕?!?! What could possibly go wrong after years of using the database with strict mode off?&lt;/p&gt;

&lt;p&gt;A lot of things apparently, so I made a list of modes I need to enable/check out before finally enabling &lt;em&gt;STRICT_TRANS_TABLES&lt;/em&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;ERROR_FOR_DIVISION_BY_ZERO&lt;/em&gt; → &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_error_for_division_by_zero" rel="noopener noreferrer"&gt;in later versions, no longer an option but part of strict mode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;NO_ZERO_IN_DATE&lt;/em&gt; → &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_no_zero_in_date" rel="noopener noreferrer"&gt;in later versions, no longer an option but part of strict mode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;NO_AUTO_CREATE_USER&lt;/em&gt; → &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_no_auto_create_user" rel="noopener noreferrer"&gt;in version 8.0, no longer an option but part of strict mode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;NO_ZERO_DATE&lt;/em&gt; → &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_no_zero_in_date" rel="noopener noreferrer"&gt;in later versions, no longer an option but part of strict mode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;ONLY_FULL_GROUP_BY&lt;/em&gt; → decided to leave it disabled&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;NO_ENGINE_SUBSTITUTION&lt;/em&gt; → was already enabled&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;STRICT_TRANS_TABLES&lt;/em&gt; → needs to be enabled&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This little side quest of cleaning up migrations, Doctrine entities which were not in sync with corresponding database tables, fixtures, using null for everything — does not matter is the field nullable or not, missing primary keys on tables etc. cost us 193,4 hours (around 33 days) just to make the application run with some additional SQL modes enabled.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Sphinx client issues
&lt;/h3&gt;

&lt;p&gt;Some legacy parts of our application still used Sphinx instead of ElasticSearch. We faced a tough choice then and there because MySQL v8.0 had a new default authentication plugin &lt;em&gt;caching_sha2_password&lt;/em&gt;, and Sphinx v2.2.4, that we were still using, uses the old authentication plugin &lt;em&gt;mysql_native_password&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Connecting to Sphinx with a user that was using &lt;em&gt;caching_sha2_password&lt;/em&gt; authentication plugin resulted in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bash-4.4$ mysql -hsphinx -usphinx 
ERROR 2003 (HY000): Can't connect to MySQL server on 'sphinx' (111)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, we tried to create a user that used the old authentication plugin. That still resulted with an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bash-4.4$ mysql -hsphinx -P9306 
ERROR 2000 (HY000): Unknown MySQL error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="http://sphinxsearch.com/docs/sphinx3.html#version-3.1.1-17-oct-2018" rel="noopener noreferrer"&gt;Issues with connecting to MySQL client 8.0+ were fixed in Sphinx v3.1.1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This left us with two possible ways to go:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Upgrade legacy parts of the application, that we all want removed, to v3.1.1 — not too much effort&lt;/li&gt;
&lt;li&gt;Remove Sphinx from the project — little sub-project&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We went with a 751 hour endeavor for 5 people and removed Sphinx from the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The magnificent world of charsets, collates and row formats
&lt;/h3&gt;

&lt;h4&gt;
  
  
  About charsets and collates
&lt;/h4&gt;

&lt;p&gt;MySQL uses &lt;em&gt;UTF8&lt;/em&gt; as an alias for the now deprecated &lt;em&gt;UTF8MB3&lt;/em&gt;. It is expected, at some point in the future, that &lt;em&gt;UTF8&lt;/em&gt; will become an alias for the &lt;em&gt;UTF8MB4&lt;/em&gt; charset. In a future MySQL release, &lt;em&gt;UTF8MB3&lt;/em&gt; should be removed. You can read more about it &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/charset-unicode-utf8mb3.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To follow the recommendation, we decided to change our charset to &lt;em&gt;UTF8MB4&lt;/em&gt;. To match our brand new charset, we had to change the collate to any one compatible with &lt;em&gt;UTF8MB4&lt;/em&gt; charset.&lt;/p&gt;

&lt;h4&gt;
  
  
  About row formats
&lt;/h4&gt;

&lt;p&gt;There are four row formats: &lt;em&gt;REDUNDANT, COMPACT, DYNAMIC&lt;/em&gt; and &lt;em&gt;COMPRESSED&lt;/em&gt;. MySQL v5.6 uses &lt;em&gt;COMPACT&lt;/em&gt; by default, and v5.7 and later use DYNAMIC.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://dev.mysql.com/doc/refman/8.0/en/innodb-row-format.html" rel="noopener noreferrer"&gt;&lt;em&gt;The DYNAMIC row format offers the same storage characteristics as the COMPACT row format but adds enhanced storage capabilities for long variable-length columns and supports large index key prefixes.&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In our code, we like to use these “cutting edge” things from the “era gone by”, meaning, we used row format &lt;em&gt;FIXED&lt;/em&gt;. It is so deprecated, that if &lt;em&gt;innodb_strict_mode&lt;/em&gt; is disabled, InnoDB issues a warning and assumes row format &lt;em&gt;DYNAMIC&lt;/em&gt;, and if innodb_strict_mode is enabled, InnoDB returns an error. We replaced &lt;em&gt;FIXED&lt;/em&gt; and &lt;em&gt;COMPACT&lt;/em&gt; row formats with &lt;em&gt;DYNAMIC&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;There was a bug. 🐛&lt;/p&gt;

&lt;p&gt;If you try to create an index on a field that exceeds 767 bytes you will get an error that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR 1709 (HY000): Index column size too large. The maximum column size is 767 bytes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it was still possible to create an index like that if you were using row format &lt;em&gt;COMPRESSED&lt;/em&gt;/&lt;em&gt;REDUNDANT&lt;/em&gt;, or you didn’t explicitly define row format &lt;em&gt;DYNAMIC&lt;/em&gt;. The result, after a server reboot, the table was inaccessible and could not be recovered. But luckily, this &lt;a href="https://bugs.mysql.com/bug.php?id=99791" rel="noopener noreferrer"&gt;issue&lt;/a&gt; was fixed in MySQL v8.0.22.&lt;/p&gt;

&lt;p&gt;If you still want to create an index on VARCHAR field, make sure the length is less or equal to 190. This is because &lt;em&gt;UTF8&lt;/em&gt; takes up to (3*255) 765 bytes, and &lt;em&gt;UTF8MB4&lt;/em&gt; takes up to (4*255) 1020 bytes.&lt;/p&gt;

&lt;h4&gt;
  
  
  The real issue
&lt;/h4&gt;

&lt;p&gt;Ok, not to hard. So we change the charset, collate and row format. Big woop, right?&lt;/p&gt;

&lt;p&gt;The real hard part was altering every single table in the production database. This will not be a problem if you do not have any huge tables, but if you do, these alters can and will take hours.&lt;/p&gt;

&lt;p&gt;The trickiest thing of all is altering all these tables with some reasonable downtime. Because, if you try to join two tables with a different collate and charset — it will fail. If you try to alter everything on one slave, and then replicate it — it will fail.&lt;/p&gt;

&lt;p&gt;Your safest bet is to backup or delete some data you can spare to reduce the table size. Or create new empty tables that will be used while the real ones are being altered and sync the deltas once it is over.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. MySQL query cache is no more
&lt;/h3&gt;

&lt;p&gt;If you rely on MySQL query cache, you will have to replace it with something else. It was deprecated in MySQL v5.7 and completely removed in MySQL v8.0.&lt;/p&gt;

&lt;p&gt;There are some alternatives, like ProxySQL query cache. But there are definitely some cutbacks.&lt;/p&gt;

&lt;p&gt;It is the simplest alternative, really easy to setup, benchmarks show better throughput — meaning performance boost. But…&lt;/p&gt;

&lt;p&gt;Unlike MySQL query cache that would invalidate the cache every time there was a write, in ProxySQL there is no way to define a way to invalidate the cache other then &lt;em&gt;cache_ttl&lt;/em&gt;. This can definitely be a limitation because there is a chance you will serve some stale data.&lt;/p&gt;

&lt;p&gt;Other then that, it does not support caching prepared statements and there is no way to manually purge the query cache. There is a parameter &lt;em&gt;mysql-query_cache_size_MB&lt;/em&gt; that defines how big your cache can get. But this is not strict, it is only used to automatically trigger the query cache purge.&lt;/p&gt;

&lt;p&gt;In any case, it just depends on whether or not this is acceptable to you. You can find more about it &lt;a href="https://www.percona.com/blog/2018/02/07/proxysql-query-cache/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are planning on upgrading MySQL, I hope you will find this helpful. The biggest problem for me was underestimating the time needed for delivering the project. I wrote this post, if for nothing else, to help you know what to look out for. :)&lt;/p&gt;




</description>
      <category>sql</category>
      <category>tips</category>
      <category>tipsandtricks</category>
      <category>mysql</category>
    </item>
    <item>
      <title>Tips&amp;Tricks for project organization</title>
      <dc:creator>Eva Marija Banaj Gađa</dc:creator>
      <pubDate>Fri, 20 Aug 2021 07:12:46 +0000</pubDate>
      <link>https://dev.to/trikoder/tips-tricks-for-project-organization-3ooa</link>
      <guid>https://dev.to/trikoder/tips-tricks-for-project-organization-3ooa</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A7GXfC3vz5O138u4t2YZuUQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A7GXfC3vz5O138u4t2YZuUQ.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At some point in your career you might find yourself leading a project. This can be stressful and hard regardless of the projects scope, especially the first time you do it.&lt;/p&gt;

&lt;p&gt;You might find it hard to be on top of everything all the time, and still be a productive developer. Sometimes it feels like things are going nowhere, deadlines are just too soon and everything will crumble around you if you don’t do everything by yourself.&lt;/p&gt;

&lt;p&gt;Here are three simple things that helped me stay sane, organized and manage my daily activities. I hope they help you too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make sure everybody has the same idea of what will be the end product of the project
&lt;/h3&gt;

&lt;p&gt;The most frustrating thing for me is having to plan something poorly specified or vaguely defined. Specification is your most prized possession while planning a project. Of course, there is no way to have everything set in stone. Requests change, things get complicated and a different approach has to be taken but, taking time to prepare and analyze everything that is requested in the specification gives you a different perspective and insight.&lt;/p&gt;

&lt;p&gt;To avoid this mess, take time to prepare for the project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Take your time to go through the specification.&lt;/strong&gt;
See what is required, what is not specified well enough and what might bite you in the… behind.
Parts that are described well probably won’t cause problems, but things that are not, need to be clarified right away. Be annoying if you have to, but find out every single detail you can possibly get your hands on.
And for those things that… khm… might bite, create analysis tasks. Take some additional time to better estimate how much work they might require.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create tasks.&lt;/strong&gt;
Doesn’t matter if you use Jira or post-it notes on the wall. Write down things from the specification that need to be done and describe them as well as you can. You will thank yourself later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Estimate tasks.&lt;/strong&gt;
Take a day or two, sit down with all people involved in the development process and estimate tasks — best case/worst case. Be careful with worst case estimates. Those are not “mildly bad” cases, they are “dog ate my code and I have to start again“ cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Milestones.&lt;/strong&gt;
Now when you know exactly what needs to be done and you have step by step tasks, divide those tasks into smaller groups. This way you get reachable goals that make it easier to follow the project timeline and keep your team motivated as there is no feeling that things are not progressing. Milestones are just small victories that eventually lead to the finished product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set the deadlines.&lt;/strong&gt;
Each milestone should have an unofficial deadline, more of a “goal date“. For calculating milestone deadlines, your gold number should be somewhere between the best and worst case estimate. Some tasks will be done sooner — some later, but in the end you will be in the ball park.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Confirm with everybody involved that the project can start and what they asked for is really what they want, what they really really want.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Make sure everybody is well informed about your project status
&lt;/h3&gt;

&lt;p&gt;Other then poorly set goals, poor communication is another source of stress. Even though sometimes it may seem like you do not have time to finish anything on time, those 30 minutes you might spend on syncing with everybody involved will make a huge difference.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Report to your team lead.&lt;/strong&gt;
Do not go to your team lead only when s**t hits the fan. Make it a habit to report the project status regularly, regardless whether things are going good or bad (or ugly). It is important he/she/other knows how things are going in this mini-team of yours. Nobody is going to be happier if things are going great, and people are getting along and working together very well. But, if that is not the case, these reports will give your team lead a chance to make some changes and help you sort things out before things get out of hand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep all involved parties up to date.&lt;/strong&gt;
If your project depends on other people or other teams, let them know how things are progressing. Sync once in a while with everyone, just to let them know how the project is going, will there be some delays, will you be done before the estimate (yeah, right…). But also, this way you will know what they did, what else they have to do, whether they have some problems…&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update the project specification.&lt;/strong&gt;
I can’t stress this enough. Yeah, it is tedious and you “don’t have time“ and you “will remember all the details from that really important meeting after two weeks“ but I really urge you to make writing things down a habit.
Keep your project specification up to date. If some requirements change, make a note of it and notify involved people there have been some changes. This way, everyone involved can easily check what is happening with the project and it gives an ensuring feeling of you being on top of everything.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Make sure you know exactly what is going on and what still needs to be done
&lt;/h3&gt;

&lt;p&gt;At any point in time you need to know what is going on. Who is doing what, what is late, what is early, is someone stuck with something, and last but not least, is your team O.K. mentally.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Be in sync with your development team.&lt;/strong&gt;
It is O.K. to offload work and let your teammates worry about things you assigned them, but that does not mean you should just let things go. It is really important to know what everybody is doing every day, how far they got, how much is there left to do and are they struggling with the given task.
Make time in your day just to hear what everybody is doing, answer any possible question they might have, debug and brainstorm ideas together.
It can get quite chaotic if everybody just does what they are assigned, without knowing what others are doing and how the whole project stands in general. There will always be pings and meetings that might interrupt you in your daily activity (programming, writing documentations, whatever else), but this way, if you dedicate time in your day just for staying on top of things and helping everybody deal with their daily activities, you will be much more productive with way less interruptions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trust your team.&lt;/strong&gt;
Don’t try to do everything yourself. You should be able to rely on your development team and believe that they will do the things you assigned to them (and that those things will be done well). Not being able to trust your development team is worse than having to do all these things yourself, because of constant worrying things won’t be done on time or won’t be done well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is one more bonus thing that I decided not to include in the list but I use it a lot. Make to-do lists. I’m not talking about daily tasks here. Make a to-do list of things that have to be accomplished, big meetings that have to happen (like a project kickoff meeting). For me, it was super satisfying to have less and less of those tasks to cross off, and it helped me organize and not forget something.&lt;/p&gt;

&lt;p&gt;With this, I conclude this post, I hope you found it useful. I hope it will help you find your own unique way of managing stressful situations and organizing your projects.&lt;/p&gt;




</description>
      <category>tips</category>
      <category>projectmanagement</category>
      <category>projects</category>
      <category>organization</category>
    </item>
    <item>
      <title>Our experience with upgrading ElasticSearch</title>
      <dc:creator>Eva Marija Banaj Gađa</dc:creator>
      <pubDate>Tue, 06 Jul 2021 08:25:57 +0000</pubDate>
      <link>https://dev.to/trikoder/our-experience-with-upgrading-elasticsearch-240p</link>
      <guid>https://dev.to/trikoder/our-experience-with-upgrading-elasticsearch-240p</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4A5x8oyX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AML0hIWv5-U6MNl1MpJo9-Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4A5x8oyX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AML0hIWv5-U6MNl1MpJo9-Q.png" alt="" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why upgrading ElasticSearch was not an easy task
&lt;/h3&gt;

&lt;p&gt;ElasticSearch, they say, packs a “ton of goodness into each release“, and if you skip a few tons of goodness, it can lead to goodness overflow that we experienced while upgrading it.&lt;/p&gt;

&lt;p&gt;One might say we had a peculiar idea of good usage of ElasticSearch mapping types, so we just used them for everything — keys in arrays, table names, search etc.&lt;/p&gt;

&lt;p&gt;That was the primary reason why the upgrade waited so long. I mean, we were stuck on version 5.3.2 aiming to jump to 7.10.1. The code depended heavily on the mapping types.&lt;/p&gt;

&lt;p&gt;Another problem entirely was the complete removal of custom plugins. One feature we had, had to be completely shut down because it needed a custom elastic plugin to perform. Luckily, it was never enabled on the production so it was no biggie, right?&lt;/p&gt;

&lt;h3&gt;
  
  
  No more mapping types, what now?
&lt;/h3&gt;

&lt;p&gt;To give you a better idea of what I’m talking about, here is a small sample of what our mappings looked like before upgrading ElasticSearch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mapping_type_1:
    active: {type: byte, index: 'true'}
    additional: {type: integer, index: 'true'}
    . . .
mapping_type_2:
    active: {type: byte, index: 'true'}
    additional: {type: integer, index: 'true'}
    . . .
. . .
mapping_type_36:
    active: {type: byte, index: 'true'}
    additional: {type: integer, index: 'true'}
    . . .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We had four indices in our 5.3.2 cluster, three of those posed no problem to upgrade. We even managed to completely remove one index because there were around 300 documents indexed in it, so there was no reason why that data could not be retrieved directly from the database.&lt;/p&gt;

&lt;p&gt;That one index that remained, had 36 mapping types that were same-same but different. At this point, we did what anyone would have done — check the &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/removal-of-types.html#_alternatives_to_mapping_types"&gt;ElasticSearch official documentation&lt;/a&gt; for the recommended procedure. And now we had two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;creating 36 different indices, one for each mapping type&lt;/li&gt;
&lt;li&gt;combining all the fields in one ultimate mapping that would cover all 36 mapping types.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We went with the second option, combining all the fields in one mapping. By doing that, we got one index with a lot, and I mean, a &lt;strong&gt;LOT&lt;/strong&gt; of fields. But it was still better that the other option, creating 36 different indices with almost identical mappings. Another argument for “one ultimate mapping option“ was the fact that we would have to cross index search all the indices without losing any performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  One mapping to rule them all
&lt;/h3&gt;

&lt;p&gt;Good. We have a course of action, what now?&lt;/p&gt;

&lt;p&gt;Let’s summarize the situation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;there is a file that contains the index mappings → let’s call it the &lt;em&gt;static mapping&lt;/em&gt; file, since those fields never change&lt;/li&gt;
&lt;li&gt;there are a 1000+ files that contain additional fields for each mapping type → let’s call these &lt;em&gt;dynamic mapping&lt;/em&gt; files, because those fields change often&lt;/li&gt;
&lt;li&gt;there are 36 tables in the database and 36 corresponding mapping types in the _static mapping _file&lt;/li&gt;
&lt;li&gt;there are 36 tables in the database that correspond to one or more &lt;em&gt;dynamic mapping&lt;/em&gt; files&lt;/li&gt;
&lt;li&gt;the code depends on the mapping types in the index to retrieve data, search etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We started the great cleanup / refactor / rewrite session to merge all those numerous &lt;em&gt;dynamic mapping&lt;/em&gt; files into one file which would then be combined with &lt;em&gt;static mappings&lt;/em&gt;. The mapping types were removed in this step, and the mapping type name was added as a new field to the &lt;em&gt;static mappings&lt;/em&gt;. That way we didn’t have to rewrite the entire application and we could use ElasticSearch 7.10.1. The new &lt;em&gt;static mappings&lt;/em&gt; file ended up looking something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_doc:
    class: {type: text, index: 'true'}
    active: {type: byte, index: 'true'}
    additional: {type: integer, index: 'true'}
    . . .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This “easy” part was followed by the removal of dependencies on mapping types across the entire code base. Hours turned to days, days to weeks, and a few weeks later we finally managed to refactor all the places that fetched mapping types from elastic and did magic with them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Better indexing procedure with zero downtime
&lt;/h3&gt;

&lt;p&gt;Indexing documents, creating and manipulating indices in any way was a whole procedure that required a hefty multi-step document. It seemed as good a time as any to refactor it.&lt;/p&gt;

&lt;p&gt;Instead of a three-page procedure we now had five console commands: &lt;em&gt;Create&lt;/em&gt;, &lt;em&gt;Delete&lt;/em&gt;, &lt;em&gt;Index&lt;/em&gt;, &lt;em&gt;Replay&lt;/em&gt; and &lt;em&gt;AddToQueue&lt;/em&gt; all of which used &lt;a href="https://github.com/ruflin/Elastica"&gt;ruflin/elastica&lt;/a&gt; to communicate with the ElasticSearch cluster in the background.&lt;/p&gt;

&lt;h4&gt;
  
  
  Queue
&lt;/h4&gt;

&lt;p&gt;The update queue is just one table in the database where the ID of the changed document and the name of the index are stored. Once the queue is enabled, any changes that go to the ElasticSearch index with the write alias are also recorded to the queue.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;AddToQueue&lt;/em&gt; command is intended to be used to easily add one or more IDs to the update queue table. This could be useful if for some reason some documents aren’t in sync with the database.&lt;/p&gt;

&lt;h4&gt;
  
  
  Replay
&lt;/h4&gt;

&lt;p&gt;The &lt;em&gt;Replay&lt;/em&gt; command then takes chunks of ids from the update queue and bulk &lt;em&gt;upserts&lt;/em&gt; (insert or update) that data into the appropriate index that has the write alias. Once the documents are updated or inserted, the records are simply deleted from the update queue table.&lt;/p&gt;

&lt;h4&gt;
  
  
  Index
&lt;/h4&gt;

&lt;p&gt;The &lt;em&gt;Index&lt;/em&gt; command creates a new index with a &lt;em&gt;write_new&lt;/em&gt; alias, enables syncing changes to the queue and bulk inserts data from the database to the index. After all documents are inserted, the &lt;em&gt;write&lt;/em&gt; alias is switched to the new index, the update queue is replayed via the Replay command, the &lt;em&gt;read&lt;/em&gt; alias is switched to the new index and the old one is deleted. And voila, indexing with zero downtime!&lt;/p&gt;

&lt;h3&gt;
  
  
  Up and running
&lt;/h3&gt;

&lt;p&gt;How are we going to deploy this huge change in a way that everything works? Once again, to the &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-upgrade.html"&gt;documentation&lt;/a&gt;! This left us with several possibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.10/rolling-upgrades.html"&gt;upgrade from 5.3 to 5.6, then do a rolling upgrade from 5.6 to 6.8 and then from 6.8 to 7.10&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.10/reindex-upgrade-remote.html"&gt;reindex from a remote cluster&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since we wanted to upgrade without downtime, we went with the second option → reindex from a remote cluster. For this to happen we had to have two parallel clusters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the old 5.3.2 cluster that is still used in production&lt;/li&gt;
&lt;li&gt;this cluster has 4 indices, and each index has both read and write alias pointing to it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E6P7Au5I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ALSoVOO9i8MC0Dx7XWSa5cQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E6P7Au5I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ALSoVOO9i8MC0Dx7XWSa5cQ.png" alt="" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;and a new empty 7.10.1 cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dLjlsMcb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2APb6gj856WoJGz0PTuGNsuw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dLjlsMcb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2APb6gj856WoJGz0PTuGNsuw.png" alt="" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We deployed the code overnight when we have the least amount of traffic on the site. To guide you through our deploy process I will list the deploy actions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploy actions&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We took one of the application servers out of the production pool, deployed new code on it and set it to connect to the new 7.10.1 cluster.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--371zzXqn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AH5JoXrnxG9vPmrNP3Zsing.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--371zzXqn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AH5JoXrnxG9vPmrNP3Zsing.png" alt="" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After that we created three new indices with the &lt;em&gt;Create&lt;/em&gt; command.
Each index had &lt;em&gt;read&lt;/em&gt; and &lt;em&gt;write&lt;/em&gt; alias pointing to it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1-dxBizp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/906/1%2AcZh7qyoN6MrlfSjnA6InFA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1-dxBizp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/906/1%2AcZh7qyoN6MrlfSjnA6InFA.png" alt="" width="800" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We enabled saving changes to the queue on the old cluster. These changes will later be replayed on the new cluster, ensuring everything is up to date and users will not be aware of the cluster switch.&lt;/li&gt;
&lt;li&gt;Now that everything is ready, we ran the &lt;em&gt;Index&lt;/em&gt; command for each index in our cluster.
The &lt;em&gt;Index&lt;/em&gt; command first created a new index with the alias &lt;em&gt;write_new&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JeLbggOT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/772/1%2ArKhXOBahPmtAyspHMeB6wQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JeLbggOT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/772/1%2ArKhXOBahPmtAyspHMeB6wQ.png" alt="" width="772" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After the index creation, the command then bulk inserted data fetched from the database into the new index. Indexing documents in all three indexes took about three hours.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2jqUC7zR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ALxu9r62HtzaIBoP5G8CSiw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2jqUC7zR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ALxu9r62HtzaIBoP5G8CSiw.png" alt="" width="800" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After indexing all documents to a single index, the indexing command switched the &lt;em&gt;write&lt;/em&gt; and &lt;em&gt;read&lt;/em&gt; alias to the new index, and the &lt;em&gt;write_new&lt;/em&gt; alias and the old index were deleted.
This was done for all indices in 7.10.1 cluster.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hrb_IYkc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A_JTPL4qOD8R27iojM8iRzA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hrb_IYkc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A_JTPL4qOD8R27iojM8iRzA.png" alt="" width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Half of the application servers had now been taken out of the production pool and the new code had been deployed on them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--njgKsWqC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ANP9DlSFlnD9wriH-gQ_bZg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--njgKsWqC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ANP9DlSFlnD9wriH-gQ_bZg.png" alt="" width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After the deploy had finished, these servers were returned to the production pool and the other half was taken out.&lt;/li&gt;
&lt;li&gt;We now ran the &lt;em&gt;Replay&lt;/em&gt; command that updates documents from the update queue, making sure users don’t see stale data for more than a few minutes.&lt;/li&gt;
&lt;li&gt;After replaying changes, we disabled syncing data to the update queue. Production now used the new cluster and all changes were saved directly into the new 7.10.1 cluster.&lt;/li&gt;
&lt;li&gt;The code was then deployed to the other half of the servers that were now out of the production pool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lbQX-bvg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A-ywuw5K5klS7krkBGrB_Eg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lbQX-bvg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A-ywuw5K5klS7krkBGrB_Eg.png" alt="" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All servers were added back to the production pool and 7.10 cluster was up and running.&lt;/li&gt;
&lt;li&gt;No new data was saved to the old cluster at this point, and it could be shut down. We decided to leave it for 24 hours as backup in case something went wrong.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--x40zUEaT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/900/1%2AfFjfjEkf916NkYwp-hPKsA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--x40zUEaT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/900/1%2AfFjfjEkf916NkYwp-hPKsA.png" alt="" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nothing went wrong. Mission accomplished!&lt;/p&gt;




</description>
      <category>elastic</category>
      <category>experience</category>
      <category>upgrade</category>
      <category>elasticsearch</category>
    </item>
  </channel>
</rss>
