<?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: Thái Lê Trí</title>
    <description>The latest articles on DEV Community by Thái Lê Trí (@trithai).</description>
    <link>https://dev.to/trithai</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%2F2548081%2Fe05373bc-14ac-4433-991a-7938c7afa475.png</url>
      <title>DEV Community: Thái Lê Trí</title>
      <link>https://dev.to/trithai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/trithai"/>
    <language>en</language>
    <item>
      <title>How to build TinyURL yourself</title>
      <dc:creator>Thái Lê Trí</dc:creator>
      <pubDate>Sun, 28 Dec 2025 09:13:59 +0000</pubDate>
      <link>https://dev.to/trithai/how-to-build-tinyurl-yourself-1lkl</link>
      <guid>https://dev.to/trithai/how-to-build-tinyurl-yourself-1lkl</guid>
      <description>&lt;p&gt;I create an side project to implement TinyURL on my own with freedom in my technology choice. &lt;/p&gt;

&lt;p&gt;From draft version to final version, performance proved to able to handle &lt;br&gt;
&lt;strong&gt;&lt;em&gt;21k read request per sec&lt;/em&gt;&lt;/strong&gt; and &lt;br&gt;
&lt;strong&gt;&lt;em&gt;14k create request per sec&lt;/em&gt;&lt;/strong&gt; with single instance. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Functional Requirement (* - Mandatory)&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create unique shorten-url from an alias creation request&lt;/li&gt;
&lt;li&gt;Redirect the access from shortenned-url to target URL&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Non-Functional Requirement (Not Mandatory, freedom part)&lt;/strong&gt;&lt;br&gt;
Note: In this project, I try to test some technology to make it fun, fast, stable, durable, and scalable&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Able to handle thousands create request per second for single instance&lt;/li&gt;
&lt;li&gt;Able to handle thousands access request per second for single instance&lt;/li&gt;
&lt;li&gt;Able to fast, and effortless detect invalid access request&lt;/li&gt;
&lt;li&gt;Create a performance test for prove the system durable, realiable, and scalable&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;First version:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple spring boot with mysql, support two api: &lt;/li&gt;
&lt;li&gt;- POST /api/alias/create&lt;/li&gt;
&lt;li&gt;- GET /{alias}&lt;/li&gt;
&lt;li&gt;which are satisfy the functional requirement stated above.&lt;/li&gt;
&lt;li&gt;Implement NanoId for create random alias&lt;/li&gt;
&lt;li&gt;Implement Snowflake ID generation and Base58 convertion from SnowflakeID to character for newly created alias&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Second version:&lt;/strong&gt;&lt;br&gt;
Add primary cache &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;using LRUCache (capacity 1000) implements from HashMap internally an instance, for fastest accessible&lt;/li&gt;
&lt;li&gt;Add secondary cache using Redis with timetolive 1 day for durable and scalable cache support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Third version:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement bloomfilter base on Redis version 8.x for fast no-exist alias check.&lt;/li&gt;
&lt;li&gt;Implement write-back every 2 sec for caching created request, empower the capacity for fast creation alias.&lt;/li&gt;
&lt;li&gt;Implement hibernate batch create, update, sequence allocation for fast saving/persisting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Final version:&lt;/strong&gt;&lt;br&gt;
Add Grafana, Prometheous, Mysql-Exporter, Redis-Exporter for monitoring&lt;br&gt;
Add K6 script for complex performance test scenario&lt;br&gt;
Add WRK script for extreme performance test scenario&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&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%2F79zhx0uijy5qyq5w068h.png" 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%2F79zhx0uijy5qyq5w068h.png" alt="Create 14k per sec" width="800" height="716"&gt;&lt;/a&gt;&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%2F0gamx6dv3ogs2fbvhg1w.png" 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%2F0gamx6dv3ogs2fbvhg1w.png" alt="Get 21k per sec" width="800" height="715"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GIT: &lt;a href="https://github.com/thailt/shorten-url" rel="noopener noreferrer"&gt;https://github.com/thailt/shorten-url&lt;/a&gt;&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>springboot</category>
      <category>performance</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>XDocReport cần plugin để tạo placeholder</title>
      <dc:creator>Thái Lê Trí</dc:creator>
      <pubDate>Sat, 27 Dec 2025 07:56:39 +0000</pubDate>
      <link>https://dev.to/trithai/xdocreport-can-plugin-de-tao-placeholder-5f2k</link>
      <guid>https://dev.to/trithai/xdocreport-can-plugin-de-tao-placeholder-5f2k</guid>
      <description>&lt;p&gt;Khi làm việc với XDocReport để populate data từ field vào các placeholder trong DocX template, một trong những thứ cần quan tâm là phải tạo các placeholder bằng plugin được phát triển bởi XDocReport. Lý do đằng sau là do Word không đảm bảo việc một word/placeholder luôn luôn được lưu trữ, tokeninze trong internal data structure (zip format, xml format) với cùng định dạng khi rendered trên word. &lt;/p&gt;

&lt;p&gt;Ví dụ: placeholder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{full_name}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;có thể được lưu trữ thành:&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;tag&amp;gt;{full&amp;lt;/tag&amp;gt;&amp;lt;tagb&amp;gt;_name}&amp;lt;/tagb&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;thay vì&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{full_name}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;vì vậy để replace placeholder bằng các hàm string replace là không hoàn toàn khả thi, và có khả năng gây lỗi bất cứ khi nào. &lt;/p&gt;

&lt;p&gt;Ref: &lt;a href="https://gpcoder.com/3185-huong-dan-xuat-du-lieu-ra-file-word-pdf-voi-xdocreport/" rel="noopener noreferrer"&gt;https://gpcoder.com/3185-huong-dan-xuat-du-lieu-ra-file-word-pdf-voi-xdocreport/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>tooling</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
