<?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: Fırat Emre ŞİRVAN</title>
    <description>The latest articles on DEV Community by Fırat Emre ŞİRVAN (@femresirvan).</description>
    <link>https://dev.to/femresirvan</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%2F989643%2F276c0970-5f9f-491a-bf87-ffbeed26b2ee.jpeg</url>
      <title>DEV Community: Fırat Emre ŞİRVAN</title>
      <link>https://dev.to/femresirvan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/femresirvan"/>
    <language>en</language>
    <item>
      <title>🔴Managing Cron Jobs with NestJS: Solving Multi-Instance Issues and Locking Mechanisms</title>
      <dc:creator>Fırat Emre ŞİRVAN</dc:creator>
      <pubDate>Thu, 14 Nov 2024 19:22:31 +0000</pubDate>
      <link>https://dev.to/femresirvan/managing-cron-jobs-with-nestjs-solving-multi-instance-issues-and-locking-mechanisms-51co</link>
      <guid>https://dev.to/femresirvan/managing-cron-jobs-with-nestjs-solving-multi-instance-issues-and-locking-mechanisms-51co</guid>
      <description>&lt;h2&gt;
  
  
  Why Cron Jobs Can Cause Problems in a Multi-Instance Environment
&lt;/h2&gt;

&lt;p&gt;Cron jobs are quite useful for running tasks at specific intervals in the background. However, in a horizontally scaled application, a cron job running simultaneously on multiple instances can lead to repetitive tasks and data inconsistencies. For example, each instance might try to update the same record or repeat a certain task, causing serious issues. To solve this, we should use locking mechanisms to ensure that tasks are only executed by a single instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with Cron Jobs in NestJS
&lt;/h2&gt;

&lt;p&gt;NestJS provides the &lt;code&gt;@nestjs/schedule&lt;/code&gt; module for cron jobs, which allows us to easily manage cron tasks. This module offers decorators to schedule cron jobs. First, let's install the module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @nestjs/schedule
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installing, we can define tasks using decorators like &lt;code&gt;@Cron&lt;/code&gt;, &lt;code&gt;@Interval&lt;/code&gt;, and &lt;code&gt;@Timeout&lt;/code&gt;. Below is an example of a simple cron job definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Cron&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CronExpression&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/schedule&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TasksService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Define a cron job that runs every minute&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Cron&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CronExpression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EVERY_MINUTE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;handleCron&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cron job running: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above, &lt;code&gt;@Cron(CronExpression.EVERY_MINUTE)&lt;/code&gt; specifies a cron job that runs every minute. With the &lt;code&gt;@Cron&lt;/code&gt; decorator, you can set different time intervals for the task. However, this job will be executed by each instance of the application if there are multiple instances. To ensure each cron job is run only once in a multi-instance environment, you need to implement locking mechanisms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Locking Mechanisms: Solutions to Multi-Instance Issues
&lt;/h2&gt;

&lt;p&gt;Locking mechanisms ensure that a specific task is executed by only one instance, preventing repeated operations or data inconsistencies. Here are the four most common locking methods:&lt;/p&gt;

&lt;h3&gt;
  
  
  Database-Based Pessimistic Lock
&lt;/h3&gt;

&lt;p&gt;A database-based pessimistic lock is often used in SQL databases. Before a task starts, a row is locked in the database, and the lock is released once the process is complete. For example, if you are using PostgreSQL or MySQL, you can lock a row using commands like &lt;code&gt;SELECT FOR UPDATE&lt;/code&gt;. This method is reliable but can cause performance issues as it requires a database connection for each cron execution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reliable and easily implemented with SQL or NoSQL databases.&lt;/li&gt;
&lt;li&gt;Provides centralized locking due to integration with database transactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can be costly in terms of database connections under heavy load.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Lock Record with Redis
&lt;/h3&gt;

&lt;p&gt;Redis offers fast and lightweight data structures, making it very effective in locking mechanisms. Before a task starts, a lock is created in Redis (e.g., by setting a key), and it is removed once the task completes. Using a timeout with Redis can prevent locks from staying open indefinitely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redis is fast and has low latency.&lt;/li&gt;
&lt;li&gt;Provides a centralized locking mechanism and is easily scalable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If there are issues with the Redis connection, locks might remain open (though a TTL can mitigate this).&lt;/li&gt;
&lt;li&gt;Requires a separate Redis service.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Monitoring with API Calls
&lt;/h3&gt;

&lt;p&gt;In some projects, tasks are monitored through a central API. Each cron job assigns a “running” status in the API before starting, and this status is updated once the job completes. The API response determines whether the task is run only once. This approach is suitable for projects requiring complex and centralized monitoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Centralized control.&lt;/li&gt;
&lt;li&gt;Makes task monitoring across systems or microservices easier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requires additional development (e.g., with a third-party app or shell scripts to make API calls).&lt;/li&gt;
&lt;li&gt;API performance can slightly affect the efficiency of cron jobs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Task Management Using Queues
&lt;/h3&gt;

&lt;p&gt;Queue-based approaches are highly effective for processing tasks in sequence and reducing duplicate operations. By using BullMQ in NestJS, each cron job can be added to a queue and processed by only one instance. Queue structures are one of the most effective solutions for task management in multi-instance environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt; Task processing in sequence and horizontal scalability compatibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt; Requires additional dependencies (Redis, BullMQ) and configuration.&lt;/p&gt;

&lt;h4&gt;
  
  
  Adding Jobs to Queue in Multi-Instance Scenarios
&lt;/h4&gt;

&lt;p&gt;In a multi-instance environment where each instance tries to add the same cron job to a queue, &lt;strong&gt;Unique Job Definitions&lt;/strong&gt; can prevent duplicate jobs. BullMQ can block jobs with the same &lt;code&gt;jobId&lt;/code&gt; from being added multiple times, ensuring that even if all instances try to queue the same job, it only gets added once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Example - Redis Lock
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Create a Redis Client
&lt;/h3&gt;

&lt;p&gt;To set up Redis, first create a Redis client using the &lt;code&gt;ioredis&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;ioredis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// redis.service.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ioredis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RedisService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Redis server host&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6379&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// Redis server port&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;getClient&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Redis&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This service allows you to use the Redis client in other files via &lt;code&gt;RedisService&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Using Redis Lock Mechanism for Unique Cron Job Execution
&lt;/h3&gt;

&lt;p&gt;Now, let's write a cron job function that implements locking using the Redis client. In this example, a lock is created in Redis before the task runs; if the lock is acquired, the job executes. Once the job is complete, the lock is released.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// task.service.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Cron&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CronExpression&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/schedule&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RedisService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./redis.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TaskService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;lockKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cron-job-lock&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Lock key&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;lockTTL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Lock TTL in seconds&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;redisService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RedisService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Cron&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CronExpression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EVERY_MINUTE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;handleCronJob&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redisService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Try acquiring the lock with NX (Only set if not exists) and EX (Expire time)&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isLocked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lockKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;locked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lockTTL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isLocked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This cron job is already running on another instance.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;// Start the job&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cron job started: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

      &lt;span class="c1"&gt;// Place your cron job logic here&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executeJob&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cron job completed.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error occurred during cron job:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lockKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Lock released.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;executeJob&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Example: 5-second delay&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus PM2
&lt;/h2&gt;

&lt;p&gt;PM2 is a popular process manager for running Node.js applications in production. It can launch applications with multiple instances, utilizing CPU cores efficiently. PM2’s cluster mode allows multiple instances of the same application to run and load balances requests across instances.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing Multiple Instances with PM2
&lt;/h3&gt;

&lt;p&gt;Using the &lt;code&gt;-i&lt;/code&gt; parameter, PM2 can start multiple instances. For example, to use the maximum CPU cores, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pm2 start app.js &lt;span class="nt"&gt;-i&lt;/span&gt; max
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command starts an instance on each CPU core. Each instance is assigned a unique ID using &lt;code&gt;NODE_APP_INSTANCE&lt;/code&gt;, enabling tasks to be tagged with this ID.&lt;/p&gt;

&lt;h3&gt;
  
  
  Drawbacks of Multiple Instance Management with PM2
&lt;/h3&gt;

&lt;p&gt;A significant drawback of PM2 is that it doesn’t provide failover or single-task control. If a job needs to be handled by only one instance, PM2 does not natively support this. In dynamic environments where instances are scaled up, PM2 doesn't continuously track the application's state, making it difficult to guarantee job consistency.&lt;/p&gt;

&lt;p&gt;PM2 provides a basic solution for multi-instance management but lacks support for single-instance cron jobs. For more reliable duplication prevention, consider using Redis-based lock mechanisms or BullMQ in Kubernetes-managed environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This guide covers a common issue with multi-instance environments and provides solutions using different locking mechanisms. While the &lt;code&gt;@nestjs/schedule&lt;/code&gt; library offers basic cron job support, it doesn't fully meet my expectations for multi-instance management as of this writing. There may be more alternatives, and I welcome any comments with suggestions on other options, including their pros and cons.&lt;/p&gt;

&lt;p&gt;Good luck 😊&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>nestjs</category>
      <category>backend</category>
    </item>
    <item>
      <title>🔴 NestJS ile Cron İşlerini Yönetmek: Çoklu Instance Sorunlarını Çözme ve Locking Mekanizmaları</title>
      <dc:creator>Fırat Emre ŞİRVAN</dc:creator>
      <pubDate>Thu, 14 Nov 2024 19:15:06 +0000</pubDate>
      <link>https://dev.to/femresirvan/nestjs-ile-cron-islerini-yonetmek-coklu-instance-sorunlarini-cozme-ve-locking-mekanizmalari-15j3</link>
      <guid>https://dev.to/femresirvan/nestjs-ile-cron-islerini-yonetmek-coklu-instance-sorunlarini-cozme-ve-locking-mekanizmalari-15j3</guid>
      <description>&lt;h2&gt;
  
  
  Çoklu Instance Ortamında Cron İşleri Neden Sorun Yaratır?
&lt;/h2&gt;

&lt;p&gt;Cron işleri, arka planda belirli aralıklarla görev çalıştırmak için çok kullanışlıdır. Ancak, yatay ölçeklemeye sahip bir uygulamada, bir cron işinin aynı anda birden fazla instance tarafından çalıştırılması, tekrarlayan işlemlere ve veri tutarsızlıklarına yol açabilir. Örneğin, her bir instance’ın aynı veri kaydını güncellemeye çalışması veya belirli bir görevi tekrarlaması ciddi sorunlara neden olabilir. Bu sorunu çözmek için locking mekanizmaları kullanarak görevlerin yalnızca tek bir instance tarafından çalıştırılmasını sağlamalıyız.&lt;/p&gt;

&lt;h2&gt;
  
  
  NestJS'de Cron İşleri ile Çalışmak
&lt;/h2&gt;

&lt;p&gt;NestJS, cron işleri için @nestjs/schedule modülünü sunar ve bu modül ile cron görevlerini kolayca yönetebiliriz. Bu modül, cron görevlerini zamanlamak için dekoratörler sağlar. Kurulum için öncelikle modülü ekleyelim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @nestjs/schedule
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modülü kurduktan sonra, @Cron, @Interval, ve @Timeout gibi dekoratörleri kullanarak görevlerimizi tanımlayabiliriz. Aşağıda, basit bir cron işinin nasıl tanımlandığını görebilirsiniz:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Cron&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CronExpression&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/schedule&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TasksService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Dakikada bir çalışan bir cron görevi tanımlayalım&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Cron&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CronExpression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EVERY_MINUTE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;handleCron&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cron job çalışıyor: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yukarıdaki örnekte, @Cron(CronExpression.EVERY_MINUTE) ifadesi, her dakika çalışacak bir cron işini belirtir. NestJS, @Cron dekoratörü ile farklı zaman aralıkları belirlemenize olanak tanır. Ancak bu iş, uygulamanızın birden fazla instance’ı olduğunda her instance tarafından çalıştırılır. Çoklu instance ortamında her cron işini yalnızca bir kez çalıştırmak için kilitleme (locking) mekanizmalarına ihtiyaç duyulur.&lt;/p&gt;

&lt;h2&gt;
  
  
  Locking Mekanizmaları: Çoklu Instance Sorunlarını Çözme Yöntemleri
&lt;/h2&gt;

&lt;p&gt;Locking mekanizmaları, belirli bir görevin yalnızca bir instance tarafından çalıştırılmasını sağlar. Böylece tekrarlanan işlemler veya veri tutarsızlıkları önlenmiş olur. İşte en yaygın kullanılan dört locking yöntemi:&lt;/p&gt;

&lt;h3&gt;
  
  
  Veritabanı ile Pessimistic Lock
&lt;/h3&gt;

&lt;p&gt;Veritabanı tabanlı pessimistic locking, özellikle SQL veritabanlarında tercih edilen bir yöntemdir. Bir görev başlamadan önce veritabanında bir satır kilitlenir ve işlem tamamlandığında bu kilit kaldırılır. Örneğin, PostgreSQL veya MySQL kullanıyorsanız, SELECT FOR UPDATE gibi komutlar ile satır kilitleyebilirsiniz. Bu yöntem oldukça güvenilirdir, ancak her cron çalışmasında veritabanı bağlantısı kurduğu için performans sorunlarına yol açabilir.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avantajlar:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Güvenilir ve SQL, NoSQL destekli veritabanlarında kolayca uygulanabilir.&lt;/li&gt;
&lt;li&gt;Veritabanı işlemlerine entegre olduğu için merkezi bir kilitleme sağlar.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Dezavantajlar:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ağır yük altında veritabanı bağlantıları açısından maliyetli olabilir.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Redis ile Lock Kaydı
&lt;/h3&gt;

&lt;p&gt;Redis, hızlı ve hafif bir veri yapısı sunar, bu nedenle locking mekanizmalarında oldukça etkilidir. Görev çalıştırılmadan önce Redis üzerinde bir kilit oluşturulup (örneğin bir anahtar değeri atanarak) görev sonunda bu kilit kaldırılır. Redis ile zaman aşımı (timeout) ekleyerek kilidin sonsuza kadar açık kalmamasını da sağlayabilirsiniz.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avantajlar:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redis hızlıdır ve düşük gecikme süresi sağlar.&lt;/li&gt;
&lt;li&gt;Merkezi bir kilit mekanizması sunar ve kolay ölçeklenebilir.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Dezavantajlar:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redis bağlantısında sorun oluşursa kilitler açık kalabilir. Tabi TTL eklediğimiz durumda bu dezavantajdan kurtulmuş oluruz.&lt;/li&gt;
&lt;li&gt;Redis gibi bir harici depolama servisine ihtiyaç duyar.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  API Call ile İzleme
&lt;/h3&gt;

&lt;p&gt;Bazı projelerde merkezi bir API aracılığıyla görevlerin durumu izlenir. Her cron işi başladığında bu API’ye bir çağrı yapılarak “çalışıyor” durumu atanır ve iş bittiğinde bu durum güncellenir. API yanıtına göre görev yalnızca bir kez çalıştırılır. Bu yöntem, daha karmaşık ve merkezi bir izleme gerektiren projeler için uygundur.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avantajlar:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Merkezi bir kontrol sağlar.&lt;/li&gt;
&lt;li&gt;Çoklu sistemler veya mikro servislerde görev izlemeyi kolaylaştırır.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Dezavantajlar:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ek geliştirme süreci gerektirir. Örneğin Api Call'ları atacak third party bir app vasıtasıyla veya curl istekleri atan sh kodlarıyla bu cron yönetilebilir.&lt;/li&gt;
&lt;li&gt;API performansı cron işlerinin verimliliğini ufak da olsa etkileyebilir.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Queue (Kuyruk) Kullanımı ile Görev Yönetimi
&lt;/h3&gt;

&lt;p&gt;Queue tabanlı yaklaşımlar, özellikle görevlerin sırayla işlenmesini sağlamada ve işlem tekrarı riskini azaltmada çok etkilidir. NestJS ile BullMQ kullanarak her cron işini bir kuyruğa ekleyebilir ve bu görevlerin yalnızca tek bir instance tarafından işlenmesini sağlayabilirsiniz. Queue yapısı, çok-instance ortamlarında görev yönetiminde en etkili çözüm yöntemlerinden biridir.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Avantajlar&lt;/strong&gt;: Görevlerin sıralı işlenmesi, yatay ölçekleme uyumu&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dezavantajlar&lt;/strong&gt;: Ek bağımlılıklar (Redis, BullMQ) ve yapılandırma ihtiyacı&lt;/p&gt;

&lt;h4&gt;
  
  
  Farklı Instance’ların Aynı İş İçin Queue’ya Ekleme Durumu
&lt;/h4&gt;

&lt;p&gt;Çoklu instance ortamında her instance’ın aynı cron işini queue'ya eklemek istemesi durumunda, görev tekrarlarını önlemek için &lt;strong&gt;Unique Job (Benzersiz Görev) Tanımlanabilir&lt;/strong&gt;. BullMQ, aynı &lt;code&gt;jobId&lt;/code&gt; ile eklenen görevlerin birden fazla eklenmesini engelleyebilir. Bu sayede tüm instance’lar aynı işi queue’ya eklemeye çalışsa bile iş yalnızca bir kez eklenmiş olur.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kod Örneği - Redis lock
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Adım 1: Redis Client Oluşturma
&lt;/h3&gt;

&lt;p&gt;İlk olarak, Redis bağlantısını ayarlamak için bir Redis client oluşturun. ioredis kütüphanesini kullanarak bağlantıyı kurabilirsiniz:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;ioredis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// redis.service.ts&lt;/span&gt;
&lt;span class="c1"&gt;// Redis/Cache servis oluşturmanın birçok yöntemi bulunmakta istediğiniz bir yöntemi entegre edebilirsiniz.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ioredis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RedisService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Redis sunucusunun host adresi&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6379&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// Redis sunucusunun port numarası&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;getClient&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Redis&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu servis, diğer dosyalarda RedisService üzerinden Redis client’ını kullanmanıza olanak tanır.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adım 2: Redis Lock Mekanizması ile Cron İşini Tekil Olarak Çalıştırma
&lt;/h3&gt;

&lt;p&gt;Şimdi Redis client’ı kullanarak kilit işlemlerini gerçekleştiren bir cron iş fonksiyonu yazalım. Bu örnekte, Redis üzerinde bir kilit oluşturuluyor; kilit alınabiliyorsa iş çalıştırılıyor. İş tamamlandığında kilit kaldırılıyor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// task.service.ts&lt;/span&gt;
&lt;span class="c1"&gt;// Ben loglarımı console.log olarak kullandım ama siz yerleşik Nestjs Logger'ı kullanabilirsiniz.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Cron&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CronExpression&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/schedule&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RedisService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./redis.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TaskService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;lockKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cron-job-lock&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Kilit anahtarı&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;lockTTL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Kilit süresi (saniye cinsinden)&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;redisService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RedisService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Cron&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CronExpression&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EVERY_MINUTE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;handleCronJob&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redisService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// NX (Only set if not exists) ve EX (Expire time) ile kilidi almayı deniyoruz.&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isLocked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lockKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;locked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EX&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lockTTL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Eğer kilit alınamazsa işlem zaten başka bir instance tarafından yürütülmektedir.&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isLocked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bu cron işi başka bir instance tarafından çalıştırılıyor.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;// İşlem başlıyor&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cron job başlatıldı: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

      &lt;span class="c1"&gt;// Buraya cron işinin yapılacağı işlemleri ekleyin&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executeJob&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cron job tamamlandı.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cron job çalıştırılırken hata oluştu:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// İşlem tamamlandığında veya hata oluştuğunda kilidi kaldır.&lt;/span&gt;
      &lt;span class="c1"&gt;// Bu kısım çok kritiktir. Eğer hata oluştuğunda kilit&lt;/span&gt;
      &lt;span class="c1"&gt;// kalkmazsa timeout süresi kadar cron çalışamaz hale gelir. &lt;/span&gt;
      &lt;span class="c1"&gt;// Bu yüzden try ve finally'yi mutlaka kullanın. Dilerseniz &lt;/span&gt;
      &lt;span class="c1"&gt;// catch kullanmayıp filterlarda yönetebilirsiniz. Daha doğru &lt;/span&gt;
      &lt;span class="c1"&gt;// bir yaklaşım olur.&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lockKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Kilit kaldırıldı.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;executeJob&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Burada cron job işlemi yapılır&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Örnek: 5 saniye bekleme&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus PM2
&lt;/h2&gt;

&lt;p&gt;PM2, Node.js uygulamalarını production ortamında yönetmek için yaygın olarak kullanılan bir process manager (işlem yöneticisi) aracıdır. Uygulamayı birden fazla instance ile çalıştırma imkanı sunarak CPU çekirdeklerini verimli kullanmayı sağlar. PM2, cluster mode ile aynı uygulamanın birden fazla örneğini başlatabilir ve load balancing (yük dengeleme) yaparak isteklere cevap verecek instance’ları otomatik olarak seçer.&lt;/p&gt;

&lt;p&gt;PM2 ile Multiple Instance Yönetimi&lt;br&gt;
PM2 ile -i parametresini kullanarak birden çok instance başlatabilirsiniz. Örneğin, maksimum CPU çekirdeğini kullanacak şekilde başlatmak için şu komut uygulanabilir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pm2 start app.js &lt;span class="nt"&gt;-i&lt;/span&gt; max
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bu komut, uygulamanın her bir CPU çekirdeğinde bir instance başlatmasını sağlar. NODE_APP_INSTANCE gibi değişkenlerle her instance’a özel bir ID atanır, böylece farklı işlemler bu ID ile tanımlanabilir.&lt;/p&gt;

&lt;h3&gt;
  
  
  PM2 ile Multiple Instance Yönetiminin Dezavantajları
&lt;/h3&gt;

&lt;p&gt;PM2’nin birden fazla instance yönetiminde önemli bir dezavantajı, kendi başına bir failover veya tekil işlem kontrolü sağlamamasıdır. Eğer bir işin yalnızca bir instance tarafından yürütülmesi gerekiyorsa, PM2 bu yönetimi doğrudan desteklemez. Dinamik ortamlarda instance’ların ölçeklendirilmesi gerektiğinde, PM2 uygulamanın durumunu sürekli takip etmediği için her instance bağımsız olarak çalışır ve iş sürekliliği garanti edilemez.&lt;/p&gt;

&lt;p&gt;PM2, multiple instance yönetiminde başlangıç seviyesinde bir çözüm sunar, ancak özellikle cron işlerinin tek bir instance’ta çalışması gerektiğinde yetersiz kalabilir. Özellikle K8s gibi araçlarda yatay ölçeklendirmeyi de yönetemiyorsanız bu yöntemi kullanmanız imkansız hale gelir. Bu tür görevler için Redis tabanlı lock mekanizmaları veya BullMQ gibi araçlar, daha güvenilir bir yapı sağlayarak tekrar eden işleri tekilleştirmek için daha uygun olacaktır.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kapanış
&lt;/h2&gt;

&lt;p&gt;Örnekleriyle önemli bir ve çok yaygın bir sorunu ele almaya çalıştım. Aslında nestjs/scheduler kütüphanesinin bunu çok daha iyi ele almasını beklerdim fakat şu tarih itibariyle beklentimi karşılamadı. Bunun gibi daha birçok alternatif olabilir. Yorumlarda siz de bulduğunuz alternatifleri avantaj ve dezavantajlarıyla bahsedebilirseniz çok memnun olurum.&lt;/p&gt;

&lt;p&gt;Kolay gelsin 😊&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>javascript</category>
      <category>backend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Nest 🆚 Express Hangi Framework'ü Seçmeliyim?</title>
      <dc:creator>Fırat Emre ŞİRVAN</dc:creator>
      <pubDate>Tue, 13 Dec 2022 17:39:28 +0000</pubDate>
      <link>https://dev.to/femresirvan/nest-vs-express-hangi-frameworku-secmeliyim-2871</link>
      <guid>https://dev.to/femresirvan/nest-vs-express-hangi-frameworku-secmeliyim-2871</guid>
      <description>&lt;h2&gt;
  
  
  Nest vs Express, Hangi Framework'ü Seçmeliyim?
&lt;/h2&gt;

&lt;p&gt;Bildiğiniz gibi son yıllarda node.js oldukça popüler. Node.js ile bir serverside application yapmak istediğimizde aklımıza ilk gelen framework Express.js oluyor. Öte yandan kendiliğinden bir template ile gelen ve çoğunlukla "opinionated" yaklaşımı benimsemiş Nest.js de son yıllarda oldukça popüler olmaya başladı.&lt;/p&gt;

&lt;p&gt;Bu karşılaştırmadaki en temel madde, Nest gibi içerisinde birçok yapıyı barındıran bir framework'ü mü tercih etmek? Yoksa custom bir biçimde kendi yapımızı oluşturabildiğimiz çok daha esnek, yalın Express ile mi devam etmek olacak.&lt;/p&gt;

&lt;p&gt;İlk olarak çok ufak Express ve Nest'i tanımlayıp akabinde hemen karşılaştırmaya geçeceğim.&lt;/p&gt;

&lt;p&gt;Dipnot: Sürekli ingilizce kavramlar kullanacağım. Kavram olarak çoğunun karşılığını Türkçede bulamadım.&lt;/p&gt;

&lt;h3&gt;
  
  
  Express.js
&lt;/h3&gt;

&lt;p&gt;Express, server-side uygulamalar oluşturmak için çok çeşitli işlevler sağlayan bir Node.js web uygulama çerçevesidir (framework). Express MVC mimarisini destekler. Fakat bunu kullanmak tamamen kullanıcıya kalır. Genel olarak "un-opinionated" bir yapı sergiler.&lt;/p&gt;

&lt;p&gt;Basit bir Express uygulaması:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const app = express();

app.get('/', (req, res) =&amp;gt; {
  res.send('Hello World!');
});

app.listen(3000, () =&amp;gt;
  console.log('Example app listening on port 3000!'),
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gördüğünüz gibi birkaç satırla bir server ayağa kaldırabilirsiniz. &lt;/p&gt;

&lt;h3&gt;
  
  
  Nest.js
&lt;/h3&gt;

&lt;p&gt;Nest de Express gibi server-side uygulamalar oluşturmamızı sağlayan bir Node.js framework'üdür. TypeScript ve JavaScript tabanlıdır. Yapı olarak Angular'ı oldukça andırır. Server-side uygulamalar oluştururken default olarak Express çatısını kullanır. Fakat platform agnostic'dir ve isterseniz Fastify gibi başka bir HTTP framework'ü de kullanabilirsiniz.&lt;/p&gt;

&lt;p&gt;Not: Eğer Nest.js kullanırken Express'i tercih ettiyseniz Express'in özelliklerini de kullanabiliyorsunuz. Fakat çoğunluğu tavsiye edilmiyor ve Express üzerindeki Nest'in bize sunduğu yapıları kullanıyoruz.&lt;/p&gt;

&lt;h3&gt;
  
  
  Karşılaştırma
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Opinionated VS Unopinionated
&lt;/h4&gt;

&lt;p&gt;Opinionated mantığı, uygun bir tarza veya bir şeyler yapma yöntemine sahip olduğu anlamına gelir. Böylece çeşitli yapılar oluştururken size kılavuz olur. Ama aynı zamanda sizin uygulama alanınızdaki serbestliğinizi de bir ölçüde kısıtlar. Öte yandan Unopinionated frameworkler ise, geliştiricilere tam bir özgürlük verir, çok çeşitlidir, daha az yapılandırılmıştır. Farklı senaryoları test etmemiz için çok uygundur fakat bir yazılım ekibinde herkes aynı yöntemi kullanmayabilir. Bu da proje dallandıkça çeşitli sorunlara yol açacaktır. Aynı zamanda projeye yeni katılacak biri de oluşturulan yapıyı kolay bir biçimde çözemeyebilir.&lt;/p&gt;

&lt;p&gt;Nest.js opinionated bir framework iken Express çoğu yerde unopinionated tutum sergiler Bir diğer deyişle Express.js daha önceden tanımlanmış (pre-defined) kurallara sahip değildir. Nest ise daha önceden tanımlı kurallara sahiptir.&lt;/p&gt;

&lt;h4&gt;
  
  
  Performans
&lt;/h4&gt;

&lt;p&gt;Nest, uzun kod satırları yazmadan doğrudan komut vermenizi sağlayan CLI (Command Line Interface) içerir. Nest tarafında yapılan Dependency Injection, uygulamanızı sorunsuz bir şekilde çalıştırmak için ihtiyaç duyduğunuz kadar çok dependencies eklemenizi sağlar. Bu gibi özelliklerle Nest hem performansı kötü etkileyecek tarafları bizim ele almamızı engellerken hem de uğraşmamız gerekmeyen birçok satır koddan kurtarır.&lt;/p&gt;

&lt;p&gt;Öte yandan Express'in asenkron yapısı ölçeklenebilir değildir.&lt;/p&gt;

&lt;h4&gt;
  
  
  Mimari (Architecture)
&lt;/h4&gt;

&lt;p&gt;İkisi arasındaki bir diğer fark da mimarileridir. Nest, MVC tasarım modelini takip ederken, Express bunu tamamen size bırakır. Çoğunlukla gördüğüm projeler MVC ile yazılmaya çalışılsa da bu da önemli bir farklılıktır. Bu da Express tarafında enterprise level bir proje daha az optimize edilmiş veya yapı olarak daha karmaşık bir hal almış olabilir.&lt;/p&gt;

&lt;p&gt;Ayrıca Nest ile kod yazarken bana hep Spring ve Angular'ı andırıyor. Daha çok OOP programming mantığını benimsiyor. Express ise daha çok FP, FRP gibi mantıklarda kod yazmamızı sağlıyor modeller harici class bile oluşturmamıza gerek kalmıyor.&lt;/p&gt;

&lt;h4&gt;
  
  
  Popülerlik
&lt;/h4&gt;

&lt;p&gt;Express'in çok daha eski bir framework olması ve hem büyük ölçekli uygulamalar için hem de küçük uygulamalar için de kullanılabilir olması onu çok daha popüler kılıyor. Bu da daha büyük bir community'ye sahip kılıyor. Ve bu da unopinionated bir mantığa sahip framework için oldukça etkili oluyor.&lt;/p&gt;

&lt;p&gt;Öte yandan Nest son 2021-2022 arasında npm ile 1 milyon ekstra haftalık indirme kazanmış.&lt;/p&gt;

&lt;h4&gt;
  
  
  Unit Testing
&lt;/h4&gt;

&lt;p&gt;Daha önce bahsettiğimiz gibi Nest, burada da bize kılavuz oluyor. Unit Test, CLI'i ve Jest'te yapılandırılmış bir varsayılan test ortamı içerdiğinden, NestJS ile oldukça kolay ve hızlıdır.&lt;/p&gt;

&lt;p&gt;Öte yandan Express içinde birçok yöntem ve başlık mevcut. Bunlardan en uygun olanı seçmek size kalmış.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sonuç Olarak
&lt;/h3&gt;

&lt;p&gt;Nest hem Java ve Angular benzeri bir yapısı ile yıllardır aşina olduğumuz OOP kavramını çokça kullanıyor.&lt;/p&gt;

&lt;p&gt;Yalın Express'i seçecek olursak geniş community'si bize çeşitli boilerplate'lerle geliyor ve her takıldığımız yerde bize kılavuz oluyor. Fakat Express custom exception handler'ından custom architecture'ına kadar her kısmın seçimini bize bırakıyor bu da gerek güvenlik gerek performans gerek de sürdürülebilirlik olarak risk içeriyor. Yani Amerika'yı yeniden keşfetmiyoruz ve bizim için en uygun Amerikayı oluşturuyoruz. Fakat hata payı da mutlaka oluyor ve bu da maliyeti doğrudan etkiliyor.&lt;/p&gt;

&lt;p&gt;Bence burada doğru bir SWOT analizi gerekiyor ve değerlendirilmesi gereken en önemli faktörler;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ekip üyelerinin yetkinlikleri&lt;/li&gt;
&lt;li&gt;Projeyi hazırlayanların sayısı&lt;/li&gt;
&lt;li&gt;Ekip üyelerinin uyumu&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;oluyor.&lt;/p&gt;

&lt;p&gt;Öte yandan hiç Express veya Nest tecrübeniz yok ise ve large scale bir application oluşturmak istiyorsanız burada önerim kesinlikle Nest olur. Nest ile çok daha az maliyetli ölçeklenebilir bir proje oluşturabilirsiniz.&lt;/p&gt;

&lt;p&gt;Aralarındaki en temel farklara bu yazımda değinmeye çalıştım. Özellikle yeni bir projeye başlarkenki karar aşaması benim için çok yıpratıcıdır.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fdcmbpballx86gt5iie0b.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fdcmbpballx86gt5iie0b.gif" width="220" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bu türkçe içerikle yükünüzü hafiflettiysem ne mutlu bana 🙂. Okuduğunuz için teşekkür ederim. Bir sonraki yazımda görüşmek üzere.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>express</category>
      <category>nestjs</category>
    </item>
  </channel>
</rss>
