<?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: ducanhkl</title>
    <description>The latest articles on DEV Community by ducanhkl (@ducanh).</description>
    <link>https://dev.to/ducanh</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%2F987758%2F8665b2bd-7d3b-46fc-abc8-b60792d22b78.jpeg</url>
      <title>DEV Community: ducanhkl</title>
      <link>https://dev.to/ducanh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ducanh"/>
    <language>en</language>
    <item>
      <title>How to deal with N+1 problems with Hibernate</title>
      <dc:creator>ducanhkl</dc:creator>
      <pubDate>Thu, 02 May 2024 15:49:40 +0000</pubDate>
      <link>https://dev.to/ducanh/how-to-deal-with-n1-problems-with-hibernate-1je9</link>
      <guid>https://dev.to/ducanh/how-to-deal-with-n1-problems-with-hibernate-1je9</guid>
      <description>&lt;h2&gt;
  
  
  1. What is the N+1 problem?
&lt;/h2&gt;

&lt;p&gt;N+1 query problem is a common performance antipattern when you use the ORM library. Specifically with Java is Hibernate&lt;br&gt;
Assume we have two entities in our system Cat and Hat. One Cat has many Hat, each Hat has only one Cat. Cat and Hat is one of many relationships.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cat 1-N Hats
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Cat {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "cat_seq_generator")
    @SequenceGenerator(name = "cat_seq_generator", sequenceName = "cat_seq_seq")
    private Long id;

    @Column(name = "name", columnDefinition = "TEXT")
    private String name;

    @OneToMany(fetch =  FetchType.LAZY, mappedBy = "cat", cascade = CascadeType.ALL)
    private List&amp;lt;Hat&amp;gt; hats;
}

public class Hat {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "hat_seq_generator")
    @SequenceGenerator(name = "hat_seq_generator", sequenceName = "hat_seq_seq")
    private Long id;

    @Column(name = "color", columnDefinition = "TEXT")
    private String color;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "cat_id", nullable = false)
    private Cat cat;
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How many queries do we need to send to the database for N cats to get the data about N cats and all hat that belongs to this cat?&lt;br&gt;
With naive implements, we will fetch all the cats, and after that, with each cat, we will get all hats belonging to this hat.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @Transactional(readOnly = true)
    public void getAllCatInfo() {
        catRepository.findAll().forEach(cat -&amp;gt; {
            log.info("Cat name: {}", cat.getName());
            log.info("Hats color: {}",
                    cat.getHats().stream().map(Hat::getColor).collect(Collectors.joining(",")));
        });;
    }

    @Test
    @DisplayName("Given cat and hats, get all, Should return ok")
    void givenCatAndHat_getAll_shouldReturnOk() {
        var cat1 = new Cat();
        cat1.setName("Tom");
        var cat2 = new Cat();
        cat2.setName("Margot");

        catRepository.saveAll(List.of(cat1, cat2));

        var hat1 = new Hat();
        var hat2 = new Hat();
        var hat3 = new Hat();
        hat1.setColor("Red");
        hat2.setColor("Blue");
        hat3.setColor("Yellow");
        hat1.setCat(cat1);
        hat2.setCat(cat2);
        hat3.setCat(cat2);
        hatRepository.saveAll(List.of(hat1, hat2, hat3));

        catService.getAllCatInfo();
    }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And look at the log, we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hibernate: select c1_0.id,c1_0.name from cats c1_0
2024-05-01T23:23:46.159+07:00  INFO 20972 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: Tom
Hibernate: select h1_0.cat_id,h1_0.id,h1_0.color from hats h1_0 where h1_0.cat_id=?
2024-05-01T23:23:46.171+07:00  INFO 20972 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hats color: Red
2024-05-01T23:23:46.171+07:00  INFO 20972 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: Margot
Hibernate: select h1_0.cat_id,h1_0.id,h1_0.color from hats h1_0 where h1_0.cat_id=?
2024-05-01T23:23:46.172+07:00  INFO 20972 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hats color: Blue,Yellow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;We need one query to fetch all cats, after that we need more N queries for each cat to get the hat information. Totaly is N+1 queries.&lt;/strong&gt;&lt;br&gt;
As you can see, we have not to query so many to do that. Hibernate provides some solutions for that problem. Which helps us reduce the time call to DB.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Specify the batch size.
&lt;/h2&gt;

&lt;p&gt;A solution is to specify &lt;code&gt;@BatchSize()&lt;/code&gt; for relations in Cat. With that annotation, hibernate will not fetch sub-entity one by one anymore. Each proxy object is involved, it will fetch all hats belonging to a &lt;code&gt;size&lt;/code&gt; cat. It will not be very efficient in the case you want to access the list not in sequential order. For example, the size is 3, and you only want access to cat number 1 and number 50, because for now, still takes 3 queries instead of 2 queries as I best solution. But don't worry, we can limit the number of cats we fetch each time. This case rarely happened in real life, I think so.&lt;br&gt;
Okay, let's add &lt;code&gt;@BatchSize&lt;/code&gt; to declare of &lt;code&gt;hats&lt;/code&gt; field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @OneToMany(fetch =  FetchType.LAZY, mappedBy = "cat", cascade = CascadeType.ALL)
    @BatchSize(size = 3)
    private List&amp;lt;Hat&amp;gt; hats;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For you to see the difference. I will create many cats and hats and see how Hibernate handles that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @Test
    @DisplayName("Given cat and hats, get all, Should return ok")
    public void givenManyCatAndHat_getAll_shouldReturnOk() {
        IntStream.range(1, 10).forEach((i) -&amp;gt; {
            var cat = new Cat();
            cat.setName(RandomStringUtils.random(4, "qwertyuio"));
            var hats = IntStream.range(1, 10).mapToObj((j) -&amp;gt; new Hat())
                    .peek((hat) -&amp;gt; hat.setColor(RandomStringUtils.random(4, "qwertyuio")))
                    .peek((hat -&amp;gt; hat.setCat(cat)))
                    .toList();
            cat.setHats(hats);
            catRepository.save(cat);
        });
        catService.getAllCatInfo();
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And lock up the log. We can see Hibernate only need one query with 3 cats.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hibernate: select c1_0.id,c1_0.name from cats c1_0
2024-05-02T21:40:58.962+07:00  INFO 9545 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: iwoo
Hibernate: select h1_0.cat_id,h1_0.id,h1_0.color from hats h1_0 where h1_0.cat_id = any (?)
2024-05-02T21:40:58.971+07:00  INFO 9545 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hats color: iqtr,ueou,ewwi,iito,iuqe,reqy,yiwr,yeoy,weru
2024-05-02T21:40:58.971+07:00  INFO 9545 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: ouoq
2024-05-02T21:40:58.971+07:00  INFO 9545 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hats color: uywy,eeqo,rtye,yiee,qwye,tury,towy,wwii,oeit
2024-05-02T21:40:58.972+07:00  INFO 9545 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: iqri
2024-05-02T21:40:58.972+07:00  INFO 9545 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hats color: qiuq,wuio,trwu,wiqe,oieo,tyet,ruew,uoyt,itri
2024-05-02T21:40:58.972+07:00  INFO 9545 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: yuyy
Hibernate: select h1_0.cat_id,h1_0.id,h1_0.color from hats h1_0 where h1_0.cat_id = any (?)
2024-05-02T21:40:58.975+07:00  INFO 9545 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hats color: wueu,qwoi,uotu,eqei,rwuo,teti,oiyq,yeqt,owuq
2024-05-02T21:40:58.975+07:00  INFO 9545 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: itoi
2024-05-02T21:40:58.975+07:00  INFO 9545 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hats color: yoqq,rure,oqoi,eoeq,etou,utyt,reew,itqw,uoqo
2024-05-02T21:40:58.975+07:00  INFO 9545 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: tqww
2024-05-02T21:40:58.975+07:00  INFO 9545 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hats color: riio,uqyi,tqoi,itut,rwwu,twou,ryew,oqeo,wiiy
2024-05-02T21:40:58.975+07:00  INFO 9545 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: ewwi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One more test case about random access&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @Transactional(readOnly = true)
    public void getAllCatAndRandomAccess() {
        List&amp;lt;Cat&amp;gt; cats = catRepository.findAll();
        IntStream.of(2, 9, 20, 30)
                .forEach((index) -&amp;gt; {
                    Cat cat = cats.get(index);
                    printCatAndHat(cat);
                });
    }

    private void printCatAndHat(Cat cat) {
        log.info("Cat name: {}", cat.getName());
        log.info("Hat colors: {}",
                cat.getHats().stream().map(Hat::getColor).collect(Collectors.joining(",")));
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the log.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Hibernate: select c1_0.id,c1_0.name from cats c1_0
2024-05-02T22:03:53.925+07:00  INFO 13132 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: iytr
Hibernate: select h1_0.cat_id,h1_0.id,h1_0.color from hats h1_0 where h1_0.cat_id = any (?)
2024-05-02T22:03:53.926+07:00  INFO 13132 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hat colors: qeww,iwoo,yyor,ertq,yrwr,etyi,errq,uwrq,iewt
2024-05-02T22:03:53.926+07:00  INFO 13132 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: qqtu
Hibernate: select h1_0.cat_id,h1_0.id,h1_0.color from hats h1_0 where h1_0.cat_id = any (?)
2024-05-02T22:03:53.927+07:00  INFO 13132 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hat colors: euir,yweo,yruq,eiou,eqei,quiu,yroy,tuwe,yuoy
2024-05-02T22:03:53.927+07:00  INFO 13132 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: qiyr
Hibernate: select h1_0.cat_id,h1_0.id,h1_0.color from hats h1_0 where h1_0.cat_id = any (?)
2024-05-02T22:03:53.928+07:00  INFO 13132 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hat colors: rroi,toti,wquq,iquu,rtui,qiti,uuqo,qeiq,yqrw
2024-05-02T22:03:53.928+07:00  INFO 13132 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: ywqe
Hibernate: select h1_0.cat_id,h1_0.id,h1_0.color from hats h1_0 where h1_0.cat_id = any (?)
2024-05-02T22:03:53.929+07:00  INFO 13132 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hat colors: yirw,eiue,wwyw,qroo,iqwq,uuuu,qttt,uttt,owir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see each time the proxy class is invoked, and one more time Hibernate reaches DB for us. &lt;/p&gt;

&lt;h2&gt;
  
  
  3. Using join fetch.
&lt;/h2&gt;

&lt;p&gt;You can also use fetch query 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;    @Query("""
        SELECT a FROM Cat a left join fetch a.hats
    """)
List&amp;lt;Cat&amp;gt; findAllCatAndPopulateHat();

    @Transactional(readOnly = true)
    public void getAllCatAndHatByJoinFetchQuery() {
        catRepository.findAllCatAndPopulateHat().forEach(this::printCatAndHat);
    }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And see the log&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Hibernate: select c1_0.id,h1_0.cat_id,h1_0.id,h1_0.color,c1_0.name from cats c1_0 left join hats h1_0 on c1_0.id=h1_0.cat_id
2024-05-02T22:12:49.092+07:00  INFO 14415 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: tweo
2024-05-02T22:12:49.092+07:00  INFO 14415 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hat colors: iuww,wiqi,eirt,yoow,woqo,itre,rruq,ywqu,wooe
2024-05-02T22:12:49.093+07:00  INFO 14415 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: etue
2024-05-02T22:12:49.093+07:00  INFO 14415 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hat colors: wqtq,eqqt,iuyy,uqyw,iiyo,yqyt,teqo,euuo,eooo
2024-05-02T22:12:49.093+07:00  INFO 14415 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: qqoq
2024-05-02T22:12:49.093+07:00  INFO 14415 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hat colors: iiqy,ytrr,ioiy,treo,tuee,teii,truq,uyyy,tequ
2024-05-02T22:12:49.093+07:00  INFO 14415 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: wqyr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hibernate will use fetch join to get all data about cat and hat in one query.&lt;br&gt;
I see it as more effective than specifying the &lt;code&gt;@BatchSize&lt;/code&gt; but this way makes the query very difficult when using page and paginations.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Specify the fetch mode.
&lt;/h2&gt;

&lt;p&gt;Hibernate provided us 3 fetch modes is&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SELECT&lt;/li&gt;
&lt;li&gt;JOIN&lt;/li&gt;
&lt;li&gt;SUBSELECT&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  - SELECT
&lt;/h3&gt;

&lt;p&gt;It uses a secondary query to load a single associated entity. The behavior is the same as above.&lt;/p&gt;
&lt;h3&gt;
  
  
  - JOIN
&lt;/h3&gt;

&lt;p&gt;This is the default behavior for fetch type EAGER&lt;br&gt;
Let's change the declaration of hats and see the difference.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @OneToMany(mappedBy = "cat", cascade = CascadeType.ALL)
    @Fetch(FetchMode.JOIN)
    private List&amp;lt;Hat&amp;gt; hats;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the log&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hibernate: select c1_0.id,c1_0.name from cats c1_0
Hibernate: select h1_0.cat_id,h1_0.id,h1_0.color from hats h1_0 where h1_0.cat_id=?
Hibernate: select h1_0.cat_id,h1_0.id,h1_0.color from hats h1_0 where h1_0.cat_id=?
2024-05-02T22:34:14.088+07:00  INFO 16360 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: Tom
2024-05-02T22:34:14.088+07:00  INFO 16360 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hat colors: Red
2024-05-02T22:34:14.088+07:00  INFO 16360 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: Margot
2024-05-02T22:34:14.088+07:00  INFO 16360 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hat colors: Blue,Yellow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We see that it will fetch all the data at the beginning of the method. This means having no subquery, and data be fetched at once. But with so many queries as needed, the n+1 problem still happened.&lt;/p&gt;

&lt;h3&gt;
  
  
  - SUBSELECT
&lt;/h3&gt;

&lt;p&gt;Let's change the code and see.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @OneToMany(fetch =  FetchType.LAZY, mappedBy = "cat", cascade = CascadeType.ALL)
    @Fetch(FetchMode.SUBSELECT)
    private List&amp;lt;Hat&amp;gt; hats;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the log&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hibernate: select c1_0.id,c1_0.name from cats c1_0
2024-05-02T22:38:30.796+07:00  INFO 16974 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: Tom
Hibernate: select h1_0.cat_id,h1_0.id,h1_0.color from hats h1_0 where h1_0.cat_id in (select c1_0.id from cats c1_0)
2024-05-02T22:38:30.802+07:00  INFO 16974 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hat colors: Red
2024-05-02T22:38:30.802+07:00  INFO 16974 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Cat name: Margot
2024-05-02T22:38:30.802+07:00  INFO 16974 --- [Demo] [    Test worker] com.example.demo.services.CatService     : Hat colors: Blue,Yellow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will issue two queries. The first one is &lt;code&gt;select c1_0.id,c1_0.name from cats c1_0&lt;/code&gt; for fetch all cat, the second one is &lt;code&gt;select h1_0.cat_id,h1_0.id,h1_0.color from hats h1_0 where h1_0.cat_id in (select c1_0.id from cats c1_0)&lt;/code&gt; for select all hat from the query. And for now, no N+1 problems anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Conclude.
&lt;/h2&gt;

&lt;p&gt;By understanding the N+1 problem and the available solutions, you can significantly improve the performance of your Hibernate applications, especially when dealing with large datasets and complex relationships.&lt;br&gt;
I hope you can choose the right solutions for your work.&lt;/p&gt;

</description>
      <category>java</category>
      <category>hibernate</category>
      <category>jpa</category>
      <category>spring</category>
    </item>
    <item>
      <title>Propagation correlationIds in Typescript</title>
      <dc:creator>ducanhkl</dc:creator>
      <pubDate>Sat, 10 Dec 2022 11:26:52 +0000</pubDate>
      <link>https://dev.to/ducanh/propagation-correlationids-in-typescript-1eei</link>
      <guid>https://dev.to/ducanh/propagation-correlationids-in-typescript-1eei</guid>
      <description>&lt;h1&gt;
  
  
  The purpose of correlationIds.
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;For checking how data transfer in one service, it for easy to check errors by watching the state of data pass through function.&lt;/li&gt;
&lt;li&gt;Check the cycle of one request in the microservice system. 
Like what is the starting point of it, the ending, and what happened when one request hit service.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  AsyncLocalStorage
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Provide by &lt;code&gt;async_hook&lt;/code&gt;. This class creates stores that stay coherent through asynchronous operations.Each instance of AsyncLocalStorage maintains an independent storage context. Multiple instances can safely exist simultaneously without the risk of interfering with each other's data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Propagation correlationIds.
&lt;/h1&gt;

&lt;h2&gt;
  
  
  UseLocalStore
&lt;/h2&gt;

&lt;p&gt;The decorator provides the storage of the function. When one function is executed, it can get this storage by calling &lt;code&gt;getLocalStoreInRun&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;const asyncLocalStorage = new AsyncLocalStorage();

export const UseLocalStore = () =&amp;gt; {
  return (
    _target: unknown,
    _propertyKey: string,
    descriptor: PropertyDescriptor
  ) =&amp;gt; {
    const original = descriptor.value;
    if (typeof original === "function") {
      descriptor.value = function (...args: unknown[]) {
        return asyncLocalStorage.run(
          {
            correlationIds: {},
          } as AsyncLocalStore,
          () =&amp;gt; {
            return original.apply(this, args);
          }
        );
      };
    } else {
      throw new Error("Only work with function");
    }
  };
};

export const getLocalStoreInRun = (): AsyncLocalStore =&amp;gt; {
  return asyncLocalStorage.getStore() as AsyncLocalStore;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  UseCorrelationId
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Simple as named, when the function begins, it will set correlation with the name be passed to this decorator. And when the function ends, it will delete the correlation.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { randomUUID } from "crypto";
import { getLocalStoreInRun } from "./use-local-store.decorator";

export const UseCorrelationId = (correlationKey: string) =&amp;gt; {
  return (
    _target: unknown,
    _propertyKey: string,
    descriptor: PropertyDescriptor
  ) =&amp;gt; {
    const original = descriptor.value;
    if (typeof original === "function") {
      descriptor.value = async function (...args: unknown[]) {
        const localStore = getLocalStoreInRun();
        if (!localStore.correlationIds[correlationKey]) {
          localStore.correlationIds[correlationKey] = randomUUID();
          const result = await original.apply(this, args);
          delete localStore.correlationIds[correlationKey];
          return result;
        } else {
          return original.apply(this, args);
        }
      };
    } else {
      throw new Error("Only work with function");
    }
  };
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test class.
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I will write a sample class to figure out that the above decorator works as expected.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export class Foo {
  public static consoleLogEnable = true;

  public async loopBar() {
    for (let i = 1; i &amp;lt; 5; i++) {
      await this.bar(i);
      this.log(`${i} loopBar`);
    }
  }

  @UseLocalStore()
  @UseCorrelationId("loopBar")
  public async loopBarWithCorrelationIds() {
    for (let i = 1; i &amp;lt; 5; i++) {
      await this.barWithCorrelationIds(i);
      this.logWithCorrelationId(`${i} loopBar`);
    }
  }

  private async bar(i: number) {
    await Promise.resolve(); // Convert function to async function
    this.log(`${i.toString()} bar`);
  }

  @UseCorrelationId("barWithCorrelationIds")
  private async barWithCorrelationIds(i: number) {
    await Promise.resolve(); // Convert function to async function
    this.logWithCorrelationId(`${i.toString()} bar`);
  }

  private logWithCorrelationId(message: string) {
    const localStore = getLocalStoreInRun();
    Foo.consoleLogEnable &amp;amp;&amp;amp;
      console.log({
        message,
        correlationIds: localStore.correlationIds,
      });
  }

  private log(message: string) {
    Foo.consoleLogEnable &amp;amp;&amp;amp;
      console.log({
        message,
      });
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Result
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  message: '1 bar',
  correlationIds: {
    loopBar: '248b1d9e-42c9-4163-80ac-4a22015447f7',
    barWithCorrelationIds: '3a4b48fb-973c-41be-9922-628db127d3f4'
  }
}
{
  message: '1 loopBar',
  correlationIds: { loopBar: '248b1d9e-42c9-4163-80ac-4a22015447f7' }
}
{
  message: '2 bar',
  correlationIds: {
    loopBar: '248b1d9e-42c9-4163-80ac-4a22015447f7',
    barWithCorrelationIds: '05a2a602-c413-4fd6-ae65-f57d492ea46d'
  }
}
{
  message: '2 loopBar',
  correlationIds: { loopBar: '248b1d9e-42c9-4163-80ac-4a22015447f7' }
}
{
  message: '3 bar',
  correlationIds: {
    loopBar: '248b1d9e-42c9-4163-80ac-4a22015447f7',
    barWithCorrelationIds: '0ada30b3-7d65-4663-a51c-a28235a9cede'
  }
}
{
  message: '3 loopBar',
  correlationIds: { loopBar: '248b1d9e-42c9-4163-80ac-4a22015447f7' }
}
{
  message: '4 bar',
  correlationIds: {
    loopBar: '248b1d9e-42c9-4163-80ac-4a22015447f7',
    barWithCorrelationIds: 'b54f4f17-256a-4c4a-b48d-1d5b115814fb'
  }
}
{
  message: '4 loopBar',
  correlationIds: { loopBar: '248b1d9e-42c9-4163-80ac-4a22015447f7' }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Benchmarks.
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Code for the benchmark.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Suite } from "benchmark";
import { Foo } from "./foo.entity";

const foo = new Foo();
const suite = new Suite();
Foo.consoleLogEnable = false;
suite
  .add("Test log with no correlationId", {
    defer: true,
    fn: async function (deferred) {
      await foo.loopBar();
      deferred.resolve();
    },
  })
  .add("Test log with correlationIds", {
    defer: true,
    fn: async function (deferred) {
      await foo.loopBarWithCorrelationIds();
      deferred.resolve();
    },
  })
  .on("cycle", function (event: { target: any }) {
    console.log(String(event.target));
  })
  .on("complete", function () {
    console.log("Done");
  })
  .run();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Result.
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Test log with no correlationId x 1,354,929 ops/sec ±1.56% (83 runs sampled)
Test log with correlationIds x 169,597 ops/sec ±2.91% (79 runs sampled)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So sad. Seem use &lt;code&gt;async_hook&lt;/code&gt; to reduce the performance six times with normal function. I had tried with other generate functions but don't have any improvement.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;I think this function can reduce the number of parameters needed to pass to the child function. But need to consider about its performance, like I benchmarked, six times is not a small number. I hope that &lt;code&gt;async_hooks&lt;/code&gt; can improve performance in the next version of nodejs.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;I'm using node 18, typescript 4.9.4, and enabling emitDecoratorMetadata.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>node</category>
      <category>typescript</category>
      <category>programming</category>
      <category>microservices</category>
    </item>
  </channel>
</rss>
