<?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>Forem: Konstantin Alaev</title>
    <description>The latest articles on Forem by Konstantin Alaev (@alaev_kostantin).</description>
    <link>https://forem.com/alaev_kostantin</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%2F2126914%2F470fe050-8454-47ac-a925-88b7d95a9f1a.jpg</url>
      <title>Forem: Konstantin Alaev</title>
      <link>https://forem.com/alaev_kostantin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/alaev_kostantin"/>
    <language>en</language>
    <item>
      <title>Universal Server Side Rendering (USSR) в Angular</title>
      <dc:creator>Konstantin Alaev</dc:creator>
      <pubDate>Sat, 28 Sep 2024 13:43:29 +0000</pubDate>
      <link>https://forem.com/alaev_kostantin/universal-server-side-rendering-ussr-v-angular-108n</link>
      <guid>https://forem.com/alaev_kostantin/universal-server-side-rendering-ussr-v-angular-108n</guid>
      <description>&lt;p&gt;В мире веб-разработки технологии постоянно эволюционируют, стремясь найти оптимальный баланс между производительностью, удобством пользователя и эффективностью разработки. Одной из ключевых технологий, прошедших значительный путь развития, является Server-Side Rendering (SSR) или рендеринг на стороне сервера. В этой эволюции фреймворк Angular сыграл значительную роль.&lt;/p&gt;

&lt;h3&gt;
  
  
  От статических страниц к динамическому контенту
&lt;/h3&gt;

&lt;p&gt;Изначально веб состоял из статических HTML-страниц, где весь контент формировался на сервере. С ростом потребности в динамическом контенте появились серверные технологии вроде PHP, ASP, и JSP, позволяющие генерировать HTML на лету.&lt;/p&gt;

&lt;h3&gt;
  
  
  Эра клиентского рендеринга и подъем Angular
&lt;/h3&gt;

&lt;p&gt;Революция в веб-разработке произошла с появлением мощных JavaScript-фреймворков, и Angular (изначально AngularJS) был одним из пионеров в этой области. Выпущенный Google в 2010 году, AngularJS предложил новый подход к созданию динамических веб-приложений с использованием клиентского рендеринга (CSR).&lt;/p&gt;

&lt;p&gt;Angular (версия 2+), выпущенный в 2016 году, продолжил эту традицию, предоставив разработчикам мощный инструментарий для создания сложных одностраничных приложений (SPA). Это позволило создавать более интерактивные и отзывчивые приложения, но также принесло новые вызовы:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Медленная начальная загрузка из-за большого размера JavaScript-бандла.&lt;/li&gt;
&lt;li&gt;Проблемы с SEO, так как поисковые роботы не всегда могли корректно индексировать динамический контент.&lt;/li&gt;
&lt;li&gt;Увеличение времени до первого значимого отображения (First Meaningful Paint).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Возрождение SSR и роль Angular Universal
&lt;/h3&gt;

&lt;p&gt;Осознав ограничения чисто клиентского подхода, сообщество Angular обратилось к идее серверного рендеринга, но уже на новом уровне. Так появился Angular Universal — официальное SSR-решение для Angular.&lt;/p&gt;

&lt;p&gt;Angular Universal позволяет рендерить приложения Angular на сервере, что решает ключевые проблемы CSR:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Улучшает начальную загрузку страницы, отправляя предварительно отрендеренный HTML.&lt;/li&gt;
&lt;li&gt;Повышает SEO, предоставляя поисковым роботам полностью сформированный контент.&lt;/li&gt;
&lt;li&gt;Улучшает производительность на мобильных устройствах и в сетях с медленным соединением.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Практика
&lt;/h2&gt;

&lt;p&gt;На словах все звучит круто, давайте попробуем это проверить.&lt;/p&gt;

&lt;p&gt;Для начала надо посмотреть что отдает приложение без SSR. Для этого надо создать "пустой" проект. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Тут и далее, код можно вставлять целиком в консоль&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# проверяем, что у нас установлен Angular CLI
npm install -g @angular/cli
# создаем наш демо проект
ng new no-ssr-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;У нас могут попросить выбрать необходимые параметры, везде жмем &lt;code&gt;enter&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;В консоле мы должны увидеть вот такой вывод&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✔ Packages installed successfully.
    Successfully initialized git.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Если все успешно, то переходим в проект и запускаем его&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd no-ssr-demo/
npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Должен получиться вот такой вывод&lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ic6l40o6oz0vo0e1gq1.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ic6l40o6oz0vo0e1gq1.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Но давайте посмотрим, что получает браузер при первом обращении к нашему сайту, для этого будем использовать &lt;code&gt;curl&lt;/code&gt;. Если вы из под винды, просто перейдите по адресу &lt;code&gt;view-source:http://localhost:4200/&lt;/code&gt;. Результат будет аналогичный&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Важно! ваш сайт может быть запушен на другом порту, если это так, просто замените 4200 на ваш порт
curl localhost:4200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Какой же мы получили вывод?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;script type="module" src="/@vite/client"&amp;gt;&amp;lt;/script&amp;gt;

  &amp;lt;meta charset="utf-8"&amp;gt;
  &amp;lt;title&amp;gt;NoSsrDemo&amp;lt;/title&amp;gt;
  &amp;lt;base href="/"&amp;gt;
  &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1"&amp;gt;
  &amp;lt;link rel="icon" type="image/x-icon" href="favicon.ico"&amp;gt;
&amp;lt;link rel="stylesheet" href="styles.css"&amp;gt;&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;app-root&amp;gt;&amp;lt;/app-root&amp;gt;
&amp;lt;script src="polyfills.js" type="module"&amp;gt;&amp;lt;/script&amp;gt;&amp;lt;script src="main.js" type="module"&amp;gt;&amp;lt;/script&amp;gt;&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;А теперь перейдем в браузер и сравним &lt;code&gt;HTML&lt;/code&gt;&lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ql6x6001qahgc01q41h.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ql6x6001qahgc01q41h.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Наш тег &lt;code&gt;app-root&lt;/code&gt; силами Angular превратился в прекрасного лебедя, которого, к сожалению, бездушный робот не увидит. Но давайте это исправим, для примера сделаем небольшой блог, который можно будет писать используя разметку &lt;code&gt;markdown&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Создание простого блога из Markdown с помощью Angular и SSR
&lt;/h2&gt;

&lt;p&gt;Если вы следовали шагам выше, это можно пропустить&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g @angular/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Создаем наш блог. На вопрос &lt;code&gt;? Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? (y/N)&lt;/code&gt; нажмите &lt;code&gt;yes&lt;/code&gt; или &lt;code&gt;y&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ng new my-blog --routing --style=scss
cd my-blog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;после успешного вывода, необходимо поставить библиотеку &lt;code&gt;marked&lt;/code&gt;, которая будет парсить наш &lt;code&gt;mardown&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install marked
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Первым делом необходимо создать отображаемые ресурсы и настроить сервер&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// папка с нашими файлами .md
const articlesFolder = resolve(serverDistFolder, '../../../articles');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ниже реализуем &lt;code&gt;API&lt;/code&gt; для их получения&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server.get('/api/articles', async (req, res) =&amp;gt; {
    // тут получение всех статей 
  });

  server.get('/api/articles/:name', (req, res) =&amp;gt; {
    const fileName = req.params.name + '.md';
    const filePath = path.join(articlesFolder, fileName);

    fs.readFile(filePath, 'utf8')
      .then((data) =&amp;gt; res.send(data))
      .catch(() =&amp;gt; res.status(404).send('Article not found'));
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;В корне проекта создайте папку &lt;code&gt;articles&lt;/code&gt; в которую положите 1-2 файла с расширением .md&lt;/p&gt;

&lt;p&gt;После запуска команды &lt;code&gt;npm run build &amp;amp;&amp;amp; npm run serve:ssr:my-blog&lt;/code&gt; сервер начнет работу (ТУТ ВНИМАТЕЛЬНО) После каждого исправления команду надо перезапускать, так что если хотим разрабатывать фронт и не писать каждый раз билд, то надо запустить ее и отдельно написать &lt;code&gt;npm run start&lt;/code&gt;, чтобы запросы не падали, надо включить &lt;code&gt;server.use(cors());&lt;/code&gt; (ну и библиотеку установить соотвествено)&lt;/p&gt;

&lt;p&gt;С грязной работой покончено, теперь у нас есть сервер, который отдает содержимое одной конкретной статьи, для всех вернет небольшое содержимое (первые 150 символов) и название файла&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Пойдем писать фронт!&lt;/strong&gt; &lt;em&gt;тут буду останавливаться только на интересных местах. Проект лежит вот тут &lt;a href="https://github.com/alaev-dev/my-blog" rel="noopener noreferrer"&gt;https://github.com/alaev-dev/my-blog&lt;/a&gt;&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Самое главное - нам &lt;strong&gt;обязательно&lt;/strong&gt; нужен резолвер. Почему? ответ в &lt;code&gt;SSR&lt;/code&gt; - нам нужны данные еще до сборки компонента&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const routes: Routes = [
  {
    path: 'article/:slug',
    component: ArticleComponent,
    resolve: {
      content: ArticleResolver,
    },
  },
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ArticleComponent&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Component({
  ...
  template: &amp;lt;div [innerHTML]="content()"&amp;gt;&amp;lt;/div&amp;gt;
})
export class ArticleComponent {
  readonly #route = inject(ActivatedRoute);

  content = toSignal(this.#route.data.pipe(map(({ content }) =&amp;gt; content)));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;И ради чего мы старались? Теперь страница выглядит вот так&lt;/p&gt;

&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faph27otq4z7fob0goo8p.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faph27otq4z7fob0goo8p.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
    </item>
    <item>
      <title>Diving Deep: The Hidden Mechanics of SQL JOINs</title>
      <dc:creator>Konstantin Alaev</dc:creator>
      <pubDate>Wed, 25 Sep 2024 21:41:51 +0000</pubDate>
      <link>https://forem.com/alaev_kostantin/diving-deep-the-hidden-mechanics-of-sql-joins-2b2f</link>
      <guid>https://forem.com/alaev_kostantin/diving-deep-the-hidden-mechanics-of-sql-joins-2b2f</guid>
      <description>&lt;h1&gt;
  
  
  How SQL JOIN Algorithms Work
&lt;/h1&gt;

&lt;p&gt;Ever wondered what's really happening when you execute a SQL JOIN? While it might seem straightforward on the surface, the underlying mechanisms are far more intricate and fascinating. In this article, we'll pull back the curtain and explore the three primary methods SQL employs to perform JOINs, each with its own strengths and ideal use cases.&lt;br&gt;
To set the stage, let's define &lt;code&gt;Na&lt;/code&gt; and &lt;code&gt;Nb&lt;/code&gt; as the number of records in tables A and B respectively. Now, let's embark on our journey through the world of SQL JOIN algorithms.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Merge Join
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Sorts both tables by the join key (if not already sorted)&lt;/li&gt;
&lt;li&gt;Compares records, moving pointers along both tables&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The overall time complexity is &lt;code&gt;O(Na log Na) + O(Nb log Nb) + O(Na + Nb)&lt;/code&gt;&lt;br&gt;
This is the method I wish for everyone when calling &lt;code&gt;EXPLAIN&lt;/code&gt;. It seems that the algorithm complexity is high, but if our values are arranged in ascending order (for example, serial ID), then there's no need for sorting, and we get a complexity of &lt;code&gt;O(Na + Nb)&lt;/code&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Hash Join
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Creates a hash table for one of the tables&lt;/li&gt;
&lt;li&gt;Goes through the second table, looking for matches in the hash table&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The overall time complexity is &lt;code&gt;O(Na) + O(Nb)&lt;/code&gt;. But we require a lot of additional memory to store the hash map.&lt;br&gt;
It's used by the DB planner if the table is large and unsorted.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Nested Loop Join
&lt;/h2&gt;

&lt;p&gt;In the case of &lt;strong&gt;Nested Loop Join&lt;/strong&gt;, the database compares each record from table A with each record from table B. This leads to an overall time complexity of &lt;code&gt;O(Na * Nb)&lt;/code&gt; - which is quadratic! How could this be allowed? But let's think about why this method exists and is sometimes even more beneficial than the others.&lt;/p&gt;

&lt;p&gt;And here's a whole rabbit hole:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Low memory in the DB (yes, this happens too), then &lt;strong&gt;Hash Join&lt;/strong&gt; becomes too expensive in terms of memory, and &lt;strong&gt;Merge Join&lt;/strong&gt; in terms of time&lt;/li&gt;
&lt;li&gt;When one or both tables involved in the join have a relatively small number of rows, &lt;strong&gt;Nested Loop Join&lt;/strong&gt; can be efficient, as iterating through all rows doesn't require significant resources (no need to create additional entities, which also takes time, as even switching registers in RAM takes time)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  BUT HOW DOES IT ALL WORK??
&lt;/h2&gt;

&lt;p&gt;The answer is simple - &lt;strong&gt;statistics&lt;/strong&gt;.&lt;br&gt;
When choosing a method, the database relies on the statistics table from the information schema, but that's not all. If during execution the real cost turns out to be erroneous, the method can be changed, but that's an idea for the next post.&lt;/p&gt;

</description>
      <category>database</category>
      <category>backend</category>
      <category>postgres</category>
    </item>
  </channel>
</rss>
