{"@attributes":{"version":"2.0"},"channel":{"title":"Phuong Le","link":"https:\/\/func25.dev\/","description":"Recent content on Phuong Le","language":"en","managingEditor":"Phuong Le","webMaster":"Phuong Le","copyright":"\u00a9 2026 Phuong Le","lastBuildDate":"Thu, 03 Sep 2026 10:00:00 +0700","item":[{"title":"How Go's Built-in Map Works with Swiss Tables","link":"https:\/\/func25.dev\/posts\/go-swiss-table-map\/","pubDate":"Thu, 03 Sep 2026 10:00:00 +0700","author":"Phuong Le","guid":"https:\/\/func25.dev\/posts\/go-swiss-table-map\/","description":"<p>If you&rsquo;re interested in how Go&rsquo;s built-in map works, I wrote a full article about it for the VictoriaMetrics blog: <a href=\"https:\/\/victoriametrics.com\/blog\/go-swiss-table-map\/\" target=\"_blank\" rel=\"noopener noreferrer\">How Go\u2019s Built-in Map Works with Swiss Tables on VictoriaMetrics \u2192<\/a><\/p>\n<p>VictoriaMetrics doesn&rsquo;t have a Go-specific newsletter yet, so I&rsquo;m sharing it here as well to help it reach more Go readers.<\/p>"},{"title":"Understanding Go's sync.Map from API to hash trie","link":"https:\/\/func25.dev\/posts\/go-sync-map-hash-trie\/","pubDate":"Wed, 26 Aug 2026 08:00:00 +0700","author":"Phuong Le","guid":"https:\/\/func25.dev\/posts\/go-sync-map-hash-trie\/","description":"<p>If you&rsquo;re interested in how <code>sync.Map<\/code> works, I wrote a full article about it for the VictoriaMetrics blog: <a href=\"https:\/\/victoriametrics.com\/blog\/go-sync-map-hash-trie\/\" target=\"_blank\" rel=\"noopener noreferrer\">Understanding Go&rsquo;s sync.Map from API to Hash Trie on VictoriaMetrics \u2192<\/a><\/p>\n<p>VictoriaMetrics doesn&rsquo;t have a Go-specific newsletter yet, so I&rsquo;m sharing it here as well to help it reach more Go readers.<\/p>"},{"title":"sync.Pool in Go explained & avoid the heap allocation mistake","link":"https:\/\/func25.dev\/posts\/go-sync-pool\/","pubDate":"Tue, 25 Aug 2026 09:00:00 +0700","author":"Phuong Le","guid":"https:\/\/func25.dev\/posts\/go-sync-pool\/","description":"<p>The discussion in this video isn&rsquo;t only about the basics of <code>sync.Pool<\/code>, but also about how it works at a higher level.<\/p>\n<ul>\n<li>How to avoid heap allocations even when using <code>sync.Pool<\/code>.<\/li>\n<li>How the empty interface works under the hood and why it matters here.<\/li>\n<li>A simple <code>bytes.Buffer<\/code> use case with <code>sync.Pool<\/code>.<\/li>\n<\/ul>\n<div style=\"position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;\">\n <iframe allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen\" loading=\"eager\" referrerpolicy=\"strict-origin-when-cross-origin\" src=\"https:\/\/www.youtube.com\/embed\/fwHok9ZhQaY?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0\" style=\"position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;\" title=\"YouTube video\"><\/iframe>\n <\/div>"},{"title":"Data races and the memory model in Go","link":"https:\/\/func25.dev\/posts\/go-memory-visibility\/","pubDate":"Mon, 17 Aug 2026 08:00:00 +0700","author":"Phuong Le","guid":"https:\/\/func25.dev\/posts\/go-memory-visibility\/","description":"<p>We write a value in one goroutine and read it in another. Nothing crashes, and the value is there. It looks like our code works.<\/p>\n<p>We actually got lucky. Go does not guarantee that one goroutine will see a write made by another unless the program explicitly coordinates their operations. This post explains what happens on a real machine, why it happens, and what we should use instead.<\/p>\n<h2 id=\"1-the-program-that-works\">1. The program that works<\/h2>\n<p>Suppose we want to pass a value from one goroutine to another without using a channel. A simple approach is to store the value in one variable and use a boolean to report when the write is complete. The reader waits for that boolean before accessing the value:<\/p>"},{"title":"How Go detects struct copies with sync.noCopy","link":"https:\/\/func25.dev\/posts\/go-sync-nocopy\/","pubDate":"Sat, 08 Aug 2026 10:00:00 +0700","author":"Phuong Le","guid":"https:\/\/func25.dev\/posts\/go-sync-nocopy\/","description":"<p>If you have read the source code of the <code>sync<\/code> package, you may have noticed that several structs contain an unusual field of type <code>noCopy<\/code>, such as <code>sync.Mutex<\/code>, <code>sync.Once<\/code>, and <code>sync.Map<\/code>:<\/p>\n<div class=\"codeblock\"><div class=\"codeblock-bar\"><span class=\"cb-lang\">go<\/span><\/div><div class=\"highlight\"><pre tabindex=\"0\" class=\"chroma\"><code class=\"language-go\" data-lang=\"go\"><span class=\"line\"><span class=\"cl\"><span class=\"kd\">type<\/span><span class=\"w\"> <\/span><span class=\"nx\">Mutex<\/span><span class=\"w\"> <\/span><span class=\"kd\">struct<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line hl\"><span class=\"cl\"><span class=\"w\">\t<\/span><span class=\"nx\">_<\/span><span class=\"w\"> <\/span><span class=\"nx\">noCopy<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">\t<\/span><span class=\"o\">...<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"kd\">type<\/span><span class=\"w\"> <\/span><span class=\"nx\">Once<\/span><span class=\"w\"> <\/span><span class=\"kd\">struct<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line hl\"><span class=\"cl\"><span class=\"w\">\t<\/span><span class=\"nx\">_<\/span><span class=\"w\"> <\/span><span class=\"nx\">noCopy<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">\t<\/span><span class=\"o\">...<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">}<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"kd\">type<\/span><span class=\"w\"> <\/span><span class=\"nx\">Map<\/span><span class=\"w\"> <\/span><span class=\"kd\">struct<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line hl\"><span class=\"cl\"><span class=\"w\">\t<\/span><span class=\"nx\">_<\/span><span class=\"w\"> <\/span><span class=\"nx\">noCopy<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"w\">\t<\/span><span class=\"o\">...<\/span><span class=\"w\">\n<\/span><\/span><\/span><span class=\"line\"><span class=\"cl\"><span class=\"p\">}<\/span><\/span><\/span><\/code><\/pre><\/div><\/div>\n<p><code>noCopy<\/code> is a special marker for types that must not be copied after their first use. But the marker itself is only an empty struct with two empty methods:<\/p>"},{"title":"Go runtime metrics: CPU, GC, memory, scheduler","link":"https:\/\/func25.dev\/posts\/go-runtime-metrics\/","pubDate":"Sat, 30 May 2026 09:00:00 +0700","author":"Phuong Le","guid":"https:\/\/func25.dev\/posts\/go-runtime-metrics\/","description":"<p>Go exposes many runtime metrics through runtime\/metrics, and many metrics clients (<a href=\"https:\/\/github.com\/prometheus\/client_golang\" target=\"_blank\" rel=\"noopener noreferrer\">Prometheus<\/a>, <a href=\"https:\/\/github.com\/VictoriaMetrics\/metrics\" target=\"_blank\" rel=\"noopener noreferrer\">VictoriaMetrics<\/a>) use it to export Go runtime stats from your application. These metrics include things like:<\/p>\n<p>how much memory your Go runtime has mapped,<\/p>\n<p>how much is used by heap objects,<\/p>\n<p>how much is free,<\/p>\n<p>how much has been returned to the OS,<\/p>\n<p>how much time goroutines have spent waiting for mutex locks,<\/p>\n<p>etc.<\/p>\n<p>Getting Go runtime stats in a few lines:<\/p>"},{"title":"Newsletter","link":"https:\/\/func25.dev\/newsletter\/","pubDate":"Sat, 30 May 2026 00:00:00 +0000","author":"Phuong Le","guid":"https:\/\/func25.dev\/newsletter\/","description":"<p>Runtime deep dives, performance notes, patterns, and interesting internals. Things worth knowing to survive production.<\/p>"},{"title":"Privacy","link":"https:\/\/func25.dev\/privacy\/","pubDate":"Sat, 30 May 2026 00:00:00 +0000","author":"Phuong Le","guid":"https:\/\/func25.dev\/privacy\/","description":"<p>This site keeps things simple.<\/p>\n<h2 id=\"analytics\">Analytics<\/h2>\n<p>This site uses <strong>Cloudflare Web Analytics<\/strong> for privacy-first page-view and performance measurement. Cloudflare Web Analytics does not use cookies or store anything in your browser.<\/p>\n<p>This site also uses <strong>Google Analytics 4<\/strong> to measure page views, engagement, outbound links, article sections reached, newsletter form views, subscription requests, and confirmed subscriptions. Google Analytics may use cookies and similar technologies to distinguish visits and produce usage reports. The site never sends your newsletter email address to Google Analytics.<\/p>"},{"title":"SIGHUP signal for configuration reloads","link":"https:\/\/func25.dev\/posts\/sighup-config-reloads\/","pubDate":"Thu, 23 Apr 2026 09:00:00 +0700","author":"Phuong Le","guid":"https:\/\/func25.dev\/posts\/sighup-config-reloads\/","description":"<p>Many applications, including Go applications by default, respond to three signals for termination: <code>SIGTERM<\/code>, <code>SIGINT<\/code>, and <code>SIGHUP<\/code>. Among these, <code>SIGHUP<\/code> carries some extra nuances and may be less relevant today.<\/p>\n<p>By definition:<\/p>\n<blockquote><p>SIGHUP (&ldquo;signal hang up&rdquo;) is a signal sent to a process when its controlling terminal is closed. \u2013 Wikipedia<\/p>\n<\/blockquote><p>But what exactly is a &ldquo;controlling terminal&rdquo;?<\/p>\n<h2 id=\"terminal\">Terminal<\/h2>\n<p>Suppose you open a terminal application. This is the window where you see text and type commands. It might be iTerm2, Terminal.app on macOS, or GNOME Terminal on Linux. This window is a graphical interface, but inside it, another program is actually running your commands. That program is usually a shell like bash or zsh.<\/p>"},{"title":"Select and for range on Go channels","link":"https:\/\/func25.dev\/posts\/select-for-range-channel\/","pubDate":"Fri, 03 Apr 2026 09:00:00 +0700","author":"Phuong Le","guid":"https:\/\/func25.dev\/posts\/select-for-range-channel\/","description":"<p>In <a href=\"https:\/\/func25.dev\/posts\/go-channels\/\">Go channels from first principles<\/a>, we went through one channel at a time: send, receive, close, and what buffered, nil, and closed channels do.<\/p>\n<p>Our code rarely waits on one channel. A worker waits for data or a shutdown signal. A request waits for a reply or a timeout. Go has 2 statements for that, and this post goes through both:<\/p>\n<ul>\n<li><code>select<\/code> waits on several channels at once,<\/li>\n<li><code>for range<\/code> drains a single channel until it closes.<\/li>\n<\/ul>\n<h2 id=\"1-select\">1. Select<\/h2>\n<p>A <code>select<\/code> waits on several channel operations and runs one that is ready:<\/p>"},{"title":"Go channels from first principles","link":"https:\/\/func25.dev\/posts\/go-channels\/","pubDate":"Thu, 02 Apr 2026 09:00:00 +0700","author":"Phuong Le","guid":"https:\/\/func25.dev\/posts\/go-channels\/","description":"<p>In <a href=\"https:\/\/func25.dev\/posts\/go-goroutines-basics\/\">Goroutines 101: A basic walkthrough<\/a>, we went through goroutines, how they run on OS threads, and what <code>GOMAXPROCS<\/code> does. Those examples did not show how 2 goroutines coordinate when they touch the same variable.<\/p>\n<p>Go has a well-known line about how to approach that, from Rob Pike&rsquo;s <a href=\"https:\/\/go-proverbs.github.io\/\" target=\"_blank\" rel=\"noopener noreferrer\">Go Proverbs<\/a>:<\/p>\n<blockquote><p>&ldquo;Don&rsquo;t communicate by sharing memory, share memory by communicating.&rdquo;<\/p>\n<\/blockquote><p>The first half describes the mutex. We put the data where both goroutines can reach it, and they take turns holding a lock so that only one of them touches it at a time. The second half describes the channel. One goroutine hands the value to the other, and that handoff is the synchronization, so there is no shared variable left to guard.<\/p>"},{"title":"Goroutines 101: A basic walkthrough","link":"https:\/\/func25.dev\/posts\/go-goroutines-basics\/","pubDate":"Wed, 01 Apr 2026 09:00:00 +0700","author":"Phuong Le","guid":"https:\/\/func25.dev\/posts\/go-goroutines-basics\/","description":"<!--\nTODO\n- Link out to the container-aware GOMAXPROCS post from the NOTE in section 3\n- Link out to the async preemption post on \"interrupt a goroutine\"\n- Link in from \/posts\/go-scheduler-mpg\/, \/posts\/go-container-aware-gomaxprocs\/ and \/posts\/go-async-preemption\/ back here (\/posts\/go-channels\/ already does)\n-->\n<p>Go makes concurrency much easier than most other languages.<\/p>\n<ul>\n<li>In Java, you decide between platform threads, virtual threads, and the thread pools of the <code>ExecutorService<\/code> framework.<\/li>\n<li>In Python, you have <code>threading<\/code>, <code>asyncio<\/code>, and <code>multiprocessing<\/code>, but which one you choose depends on whether the work waits for I\/O or uses the CPU.<\/li>\n<\/ul>\n<p>In Go, you just write 1 keyword (<code>go<\/code>) in front of a function call:<\/p>"}]}}