chore: format using yarn prettier
parent
c866394314
commit
6d8a50e8a9
|
@ -12,3 +12,6 @@
|
|||
|
||||
# GitHub
|
||||
.github_access_token
|
||||
|
||||
# Node
|
||||
node_modules
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# gitignore
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# Window
|
||||
*.exe
|
||||
|
||||
# IntelliJ
|
||||
.idea
|
||||
|
||||
# VSCode
|
||||
.vscode
|
||||
|
||||
# GitHub
|
||||
.github_access_token
|
||||
|
||||
# Node
|
||||
node_modules
|
||||
|
||||
# Ignore prettier
|
||||
.github
|
|
@ -0,0 +1 @@
|
|||
{}
|
1
Makefile
1
Makefile
|
@ -27,6 +27,7 @@ format:
|
|||
go install mvdan.cc/gofumpt@latest
|
||||
gofimports -w -company github.com/make-go-great .
|
||||
gofumpt -w -extra .
|
||||
yarn prettier --write .
|
||||
|
||||
gen:
|
||||
go run .
|
||||
|
|
|
@ -43,55 +43,173 @@
|
|||
<div>
|
||||
<a href="index">Index</a>
|
||||
</div>
|
||||
<h1><a id="user-content-backup-my-way" class="anchor" aria-hidden="true" href="#backup-my-way"><span aria-hidden="true" class="octicon octicon-link"></span></a>Backup my way</h1>
|
||||
<p>First thing first, I want to list my own devices, which I have through the years:</p>
|
||||
<ul>
|
||||
<li><del>Laptop Samsung NP300E4Z-S06VN (Old laptop which I give to my mom)</del></li>
|
||||
<li><del><a href="https://www.dell.com/support/home/en-vn/product-support/product/inspiron-15-3567-laptop/drivers" rel="nofollow">Laptop Dell Inspiron 15 3567</a> (My mom bought it for me when I go to college, I give it to my mom afterward)</del></li>
|
||||
<li><del><a href="https://www.acer.com/ac/en/US/content/support-product/8841" rel="nofollow">Laptop Acer Nitro AN515-45</a> (Gaming laptop which I buy for gaming, I give it to my sister)</del></li>
|
||||
<li>MacBook Pro M1 2020 (My company laptop)</li>
|
||||
<li><del>Phone <a href="https://forum.xda-developers.com/c/lg-g3.3147/" rel="nofollow">LG G3</a> (Bought long time ago, now is a brick)</del></li>
|
||||
<li><del>Phone <a href="https://forum.xda-developers.com/c/xiaomi-redmi-6a.7881/" rel="nofollow">Xiaomi Redmi 6A</a> (I give it to my sister too)</del></li>
|
||||
<li>Phone <a href="https://forum.xda-developers.com/c/xiaomi-poco-x3-nfc.11523/" rel="nofollow">Xiaomi Poco X3 NFC</a> (Primary phone which I use daily)</li>
|
||||
</ul>
|
||||
<p>App/Service I use daily:</p>
|
||||
<ul>
|
||||
<li><a href="https://bitwarden.com/" rel="nofollow">Bitwarden</a></li>
|
||||
<li><a href="https://getaegis.app/" rel="nofollow">Aegis Authenticator</a></li>
|
||||
<li><a href="https://rclone.org/" rel="nofollow">Rclone</a></li>
|
||||
<li><a href="https://restic.net/" rel="nofollow">restic</a></li>
|
||||
<li><a href="https://tailscale.com/" rel="nofollow">Tailscale</a></li>
|
||||
<li>GitHub / GitLab</li>
|
||||
<li>Google Keep / Notion</li>
|
||||
<li>Google Drive (I use 200GB plan)</li>
|
||||
</ul>
|
||||
<p>The purpose is that I want my data to be safe, secure, and can be easily recovered if I lost some devices;
|
||||
or in the worst situation, I lost all.
|
||||
Because you know, it is hard to guess what is waiting for us in the future.</p>
|
||||
<p>There are 2 sections which I want to share, the first is <strong>How to backup</strong>, the second is <strong>Recover strategy</strong>.</p>
|
||||
<h2><a id="user-content-how-to-backup" class="anchor" aria-hidden="true" href="#how-to-backup"><span aria-hidden="true" class="octicon octicon-link"></span></a>How to backup</h2>
|
||||
<p>Before I talk about backup, I want to talk about data.
|
||||
In specifically, which data should I backup?</p>
|
||||
<p>I use Arch Linux and macOS, primarily work in the terminal so I have too many dotfiles, for example, <code>~/.config/nvim/init.lua</code>.
|
||||
Each time I reinstall Arch Linux (I like it a lot), I need to reconfigure all the settings, and it is time-consuming.</p>
|
||||
<p>So for the DE and UI settings, I keep it as default as possible, unless it's getting in my way, I leave the default setting there and forget about it.
|
||||
The others are dotfiles, which I write my own <a href="https://github.com/haunt98/dotfiles">dotfiles tool</a> to backup and reconfigure easily and quickly.
|
||||
Also, I know that installing Arch Linux is not easy, despite I install it too many times (Like thousand times since I was in high school).
|
||||
Not because it is hard, but as life goes on, the <a href="https://wiki.archlinux.org/title/installation_guide" rel="nofollow">official install guide</a> keeps getting new update and covering too many cases for my own personal use, so I write my own <a href="https://github.com/haunt98/til/blob/main/install-archlinux.md">guide</a> to quickly capture what I need to do.
|
||||
I back up all my dotfiles in GitHub and GitLab as I trust them both.
|
||||
Also as I travel the Internet, I discover <a href="https://codeberg.org/" rel="nofollow">Codeberg</a> and <a href="https://gitea.treehouse.systems/" rel="nofollow">Treehouse</a> and use them as another backup for git repo.</p>
|
||||
<p>So that is my dotfiles, for my regular data, like Wallpaper or Books, Images, I use Google Drive (Actually I pay for it).
|
||||
But the step: open the webpage, click the upload button and choose files seems boring and time-consuming.
|
||||
So I use Rclone, it supports Google Drive, One Drive and many providers but I only use Google Drive for now.
|
||||
The commands are simple:</p>
|
||||
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#</span> Sync from local to remote</span>
|
||||
<h1>
|
||||
<a
|
||||
id="user-content-backup-my-way"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#backup-my-way"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Backup my way
|
||||
</h1>
|
||||
<p>
|
||||
First thing first, I want to list my own devices, which I have through the
|
||||
years:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<del
|
||||
>Laptop Samsung NP300E4Z-S06VN (Old laptop which I give to my
|
||||
mom)</del
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<del
|
||||
><a
|
||||
href="https://www.dell.com/support/home/en-vn/product-support/product/inspiron-15-3567-laptop/drivers"
|
||||
rel="nofollow"
|
||||
>Laptop Dell Inspiron 15 3567</a
|
||||
>
|
||||
(My mom bought it for me when I go to college, I give it to my mom
|
||||
afterward)</del
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<del
|
||||
><a
|
||||
href="https://www.acer.com/ac/en/US/content/support-product/8841"
|
||||
rel="nofollow"
|
||||
>Laptop Acer Nitro AN515-45</a
|
||||
>
|
||||
(Gaming laptop which I buy for gaming, I give it to my sister)</del
|
||||
>
|
||||
</li>
|
||||
<li>MacBook Pro M1 2020 (My company laptop)</li>
|
||||
<li>
|
||||
<del
|
||||
>Phone
|
||||
<a
|
||||
href="https://forum.xda-developers.com/c/lg-g3.3147/"
|
||||
rel="nofollow"
|
||||
>LG G3</a
|
||||
>
|
||||
(Bought long time ago, now is a brick)</del
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<del
|
||||
>Phone
|
||||
<a
|
||||
href="https://forum.xda-developers.com/c/xiaomi-redmi-6a.7881/"
|
||||
rel="nofollow"
|
||||
>Xiaomi Redmi 6A</a
|
||||
>
|
||||
(I give it to my sister too)</del
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
Phone
|
||||
<a
|
||||
href="https://forum.xda-developers.com/c/xiaomi-poco-x3-nfc.11523/"
|
||||
rel="nofollow"
|
||||
>Xiaomi Poco X3 NFC</a
|
||||
>
|
||||
(Primary phone which I use daily)
|
||||
</li>
|
||||
</ul>
|
||||
<p>App/Service I use daily:</p>
|
||||
<ul>
|
||||
<li><a href="https://bitwarden.com/" rel="nofollow">Bitwarden</a></li>
|
||||
<li>
|
||||
<a href="https://getaegis.app/" rel="nofollow">Aegis Authenticator</a>
|
||||
</li>
|
||||
<li><a href="https://rclone.org/" rel="nofollow">Rclone</a></li>
|
||||
<li><a href="https://restic.net/" rel="nofollow">restic</a></li>
|
||||
<li><a href="https://tailscale.com/" rel="nofollow">Tailscale</a></li>
|
||||
<li>GitHub / GitLab</li>
|
||||
<li>Google Keep / Notion</li>
|
||||
<li>Google Drive (I use 200GB plan)</li>
|
||||
</ul>
|
||||
<p>
|
||||
The purpose is that I want my data to be safe, secure, and can be easily
|
||||
recovered if I lost some devices; or in the worst situation, I lost all.
|
||||
Because you know, it is hard to guess what is waiting for us in the
|
||||
future.
|
||||
</p>
|
||||
<p>
|
||||
There are 2 sections which I want to share, the first is
|
||||
<strong>How to backup</strong>, the second is
|
||||
<strong>Recover strategy</strong>.
|
||||
</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-how-to-backup"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#how-to-backup"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>How to backup
|
||||
</h2>
|
||||
<p>
|
||||
Before I talk about backup, I want to talk about data. In specifically,
|
||||
which data should I backup?
|
||||
</p>
|
||||
<p>
|
||||
I use Arch Linux and macOS, primarily work in the terminal so I have too
|
||||
many dotfiles, for example, <code>~/.config/nvim/init.lua</code>. Each
|
||||
time I reinstall Arch Linux (I like it a lot), I need to reconfigure all
|
||||
the settings, and it is time-consuming.
|
||||
</p>
|
||||
<p>
|
||||
So for the DE and UI settings, I keep it as default as possible, unless
|
||||
it's getting in my way, I leave the default setting there and forget about
|
||||
it. The others are dotfiles, which I write my own
|
||||
<a href="https://github.com/haunt98/dotfiles">dotfiles tool</a> to backup
|
||||
and reconfigure easily and quickly. Also, I know that installing Arch
|
||||
Linux is not easy, despite I install it too many times (Like thousand
|
||||
times since I was in high school). Not because it is hard, but as life
|
||||
goes on, the
|
||||
<a
|
||||
href="https://wiki.archlinux.org/title/installation_guide"
|
||||
rel="nofollow"
|
||||
>official install guide</a
|
||||
>
|
||||
keeps getting new update and covering too many cases for my own personal
|
||||
use, so I write my own
|
||||
<a href="https://github.com/haunt98/til/blob/main/install-archlinux.md"
|
||||
>guide</a
|
||||
>
|
||||
to quickly capture what I need to do. I back up all my dotfiles in GitHub
|
||||
and GitLab as I trust them both. Also as I travel the Internet, I discover
|
||||
<a href="https://codeberg.org/" rel="nofollow">Codeberg</a> and
|
||||
<a href="https://gitea.treehouse.systems/" rel="nofollow">Treehouse</a>
|
||||
and use them as another backup for git repo.
|
||||
</p>
|
||||
<p>
|
||||
So that is my dotfiles, for my regular data, like Wallpaper or Books,
|
||||
Images, I use Google Drive (Actually I pay for it). But the step: open the
|
||||
webpage, click the upload button and choose files seems boring and
|
||||
time-consuming. So I use Rclone, it supports Google Drive, One Drive and
|
||||
many providers but I only use Google Drive for now. The commands are
|
||||
simple:
|
||||
</p>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre><span class="pl-c"><span class="pl-c">#</span> Sync from local to remote</span>
|
||||
rclone sync MyBooks remote:MyBooks -P --exclude .DS_Store
|
||||
|
||||
<span class="pl-c"><span class="pl-c">#</span> Sync from remote to local</span>
|
||||
rclone sync remote:MyBooks MyBooks -P --exclude .DS_Store</pre></div>
|
||||
<p>Before you use Rclone to sync to Google Drive, you should read <a href="https://rclone.org/drive/" rel="nofollow">Google Drive rclone configuration</a> first.</p>
|
||||
<p>For private data, I use restic which can be used with Rclone:</p>
|
||||
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#</span> Init</span>
|
||||
rclone sync remote:MyBooks MyBooks -P --exclude .DS_Store</pre>
|
||||
</div>
|
||||
<p>
|
||||
Before you use Rclone to sync to Google Drive, you should read
|
||||
<a href="https://rclone.org/drive/" rel="nofollow"
|
||||
>Google Drive rclone configuration</a
|
||||
>
|
||||
first.
|
||||
</p>
|
||||
<p>For private data, I use restic which can be used with Rclone:</p>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre><span class="pl-c"><span class="pl-c">#</span> Init</span>
|
||||
restic -r rclone:remote:PrivateData init
|
||||
|
||||
<span class="pl-c"><span class="pl-c">#</span> Backup</span>
|
||||
|
@ -101,27 +219,74 @@ restic -r rclone:remote:PrivateData backup PrivateData
|
|||
restic -r rclone:remote:PrivateData forget --keep-last 1 --prune
|
||||
|
||||
<span class="pl-c"><span class="pl-c">#</span> Restore</span>
|
||||
restic -r rclone:remote:PrivateData restore latest --target <span class="pl-k">~</span></pre></div>
|
||||
<p>The next data is my passwords and my OTPs.
|
||||
These are the things which I'm scare to lose the most.
|
||||
First thing first, I enable 2-Step Verification for all of my important accounts, should use both OTP and phone method.</p>
|
||||
<p>I use Bitwarden for passwords (That is a long story, coming from Google Password manager to Firefox Lockwise and then settle down with Bitwarden) and Aegis for OTPs.
|
||||
The reason I choose Aegis, not Authy (I use Authy for so long but Aegis is definitely better) is because Aegis allows me to extract all the OTPs to a single file (Can be encrypted), which I use to transfer or backup easily.</p>
|
||||
<p>As long as Bitwarden provides free passwords stored, I use all of its apps, extensions so that I can easily sync passwords between laptops and phones.
|
||||
The thing I need to remember is the master password of Bitwarden in my head.</p>
|
||||
<p>With Aegis, I export the data, then sync it to Google Drive, also store it locally in my phone.</p>
|
||||
<p>The main problem here is the OTP, I can not store all of my OTPs in the cloud completely.
|
||||
Because if I want to access my OTPs in the cloud, I should log in, and then input my OTP, this is a circle, my friends.</p>
|
||||
<h2><a id="user-content-recovery-strategy" class="anchor" aria-hidden="true" href="#recovery-strategy"><span aria-hidden="true" class="octicon octicon-link"></span></a>Recovery strategy</h2>
|
||||
<p>There are many strategies that I process to react as if something strange is happening to my devices.</p>
|
||||
<p>If I lost my laptops, single laptop or all, do not panic as long as I have my phones.
|
||||
The OTPs are in there, the passwords are in Bitwarden cloud, other data is in Google Drive so nothing is lost here.</p>
|
||||
<p>If I lost my phone, but not my laptops, I use the OTPs which are stored locally in my laptops.</p>
|
||||
<p>In the worst situation, I lost everything, my laptops, my phone.
|
||||
The first step is to recover my SIM, then log in to Google account using the password and SMS OTP.
|
||||
After that, log in to Bitwarden account using the master password and OTP from Gmail, which I open previously.</p>
|
||||
<h2><a id="user-content-the-end" class="anchor" aria-hidden="true" href="#the-end"><span aria-hidden="true" class="octicon octicon-link"></span></a>The end</h2>
|
||||
<p>This guide will be updated regularly I promise.</p>
|
||||
restic -r rclone:remote:PrivateData restore latest --target <span class="pl-k">~</span></pre>
|
||||
</div>
|
||||
<p>
|
||||
The next data is my passwords and my OTPs. These are the things which I'm
|
||||
scare to lose the most. First thing first, I enable 2-Step Verification
|
||||
for all of my important accounts, should use both OTP and phone method.
|
||||
</p>
|
||||
<p>
|
||||
I use Bitwarden for passwords (That is a long story, coming from Google
|
||||
Password manager to Firefox Lockwise and then settle down with Bitwarden)
|
||||
and Aegis for OTPs. The reason I choose Aegis, not Authy (I use Authy for
|
||||
so long but Aegis is definitely better) is because Aegis allows me to
|
||||
extract all the OTPs to a single file (Can be encrypted), which I use to
|
||||
transfer or backup easily.
|
||||
</p>
|
||||
<p>
|
||||
As long as Bitwarden provides free passwords stored, I use all of its
|
||||
apps, extensions so that I can easily sync passwords between laptops and
|
||||
phones. The thing I need to remember is the master password of Bitwarden
|
||||
in my head.
|
||||
</p>
|
||||
<p>
|
||||
With Aegis, I export the data, then sync it to Google Drive, also store it
|
||||
locally in my phone.
|
||||
</p>
|
||||
<p>
|
||||
The main problem here is the OTP, I can not store all of my OTPs in the
|
||||
cloud completely. Because if I want to access my OTPs in the cloud, I
|
||||
should log in, and then input my OTP, this is a circle, my friends.
|
||||
</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-recovery-strategy"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#recovery-strategy"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Recovery strategy
|
||||
</h2>
|
||||
<p>
|
||||
There are many strategies that I process to react as if something strange
|
||||
is happening to my devices.
|
||||
</p>
|
||||
<p>
|
||||
If I lost my laptops, single laptop or all, do not panic as long as I have
|
||||
my phones. The OTPs are in there, the passwords are in Bitwarden cloud,
|
||||
other data is in Google Drive so nothing is lost here.
|
||||
</p>
|
||||
<p>
|
||||
If I lost my phone, but not my laptops, I use the OTPs which are stored
|
||||
locally in my laptops.
|
||||
</p>
|
||||
<p>
|
||||
In the worst situation, I lost everything, my laptops, my phone. The first
|
||||
step is to recover my SIM, then log in to Google account using the
|
||||
password and SMS OTP. After that, log in to Bitwarden account using the
|
||||
master password and OTP from Gmail, which I open previously.
|
||||
</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-the-end"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#the-end"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>The end
|
||||
</h2>
|
||||
<p>This guide will be updated regularly I promise.</p>
|
||||
|
||||
<div>
|
||||
Feel free to ask me via
|
||||
|
|
|
@ -43,11 +43,22 @@
|
|||
<div>
|
||||
<a href="index">Index</a>
|
||||
</div>
|
||||
<h1><a id="user-content-dockerfile-for-go" class="anchor" aria-hidden="true" href="#dockerfile-for-go"><span aria-hidden="true" class="octicon octicon-link"></span></a>Dockerfile for Go</h1>
|
||||
<p>Each time I start a new Go project, I repeat many steps.
|
||||
Like set up <code>.gitignore</code>, CI configs, Dockerfile, ...</p>
|
||||
<p>So I decide to have a baseline Dockerfile like this:</p>
|
||||
<div class="highlight highlight-source-dockerfile"><pre><span class="pl-k">FROM</span> golang:1.19-bullseye as builder
|
||||
<h1>
|
||||
<a
|
||||
id="user-content-dockerfile-for-go"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#dockerfile-for-go"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Dockerfile for Go
|
||||
</h1>
|
||||
<p>
|
||||
Each time I start a new Go project, I repeat many steps. Like set up
|
||||
<code>.gitignore</code>, CI configs, Dockerfile, ...
|
||||
</p>
|
||||
<p>So I decide to have a baseline Dockerfile like this:</p>
|
||||
<div class="highlight highlight-source-dockerfile">
|
||||
<pre><span class="pl-k">FROM</span> golang:1.19-bullseye as builder
|
||||
|
||||
<span class="pl-k">RUN</span> go install golang.org/dl/go1.19@latest \
|
||||
&& go1.19 download
|
||||
|
@ -65,46 +76,95 @@ Like set up <code>.gitignore</code>, CI configs, Dockerfile, ...</p>
|
|||
|
||||
<span class="pl-k">COPY</span> --from=builder /build/app /app
|
||||
|
||||
<span class="pl-k">ENTRYPOINT</span> [<span class="pl-s">"/app"</span>]</pre></div>
|
||||
<p>I use <a href="https://docs.docker.com/develop/develop-images/multistage-build/" rel="nofollow">multi-stage build</a> to keep my image size small.
|
||||
First stage is <a href="https://hub.docker.com/_/golang" rel="nofollow">Go official image</a>,
|
||||
second stage is <a href="https://github.com/GoogleContainerTools/distroless">Distroless</a>.</p>
|
||||
<p>Before Distroless, I use <a href="https://hub.docker.com/_/alpine" rel="nofollow">Alpine official image</a>,
|
||||
There is a whole discussion on the Internet to choose which is the best base image for Go.
|
||||
After reading some blogs, I discover Distroless as a small and secure base image.
|
||||
So I stick with it for a while.</p>
|
||||
<p>Also, remember to match Distroless Debian version with Go official image Debian version.</p>
|
||||
<div class="highlight highlight-source-dockerfile"><pre><span class="pl-k">FROM</span> golang:1.19-bullseye as builder</pre></div>
|
||||
<p>This is Go image I use as a build stage.
|
||||
This can be official Go image or custom image is required in some companies.</p>
|
||||
<div class="highlight highlight-source-dockerfile"><pre><span class="pl-k">RUN</span> go install golang.org/dl/go1.19@latest \
|
||||
&& go1.19 download</pre></div>
|
||||
<p>This is optional.
|
||||
In my case, my company is slow to update Go image so I use this trick to install latest Go version.</p>
|
||||
<div class="highlight highlight-source-dockerfile"><pre><span class="pl-k">WORKDIR</span> /build
|
||||
<span class="pl-k">ENTRYPOINT</span> [<span class="pl-s">"/app"</span>]</pre>
|
||||
</div>
|
||||
<p>
|
||||
I use
|
||||
<a
|
||||
href="https://docs.docker.com/develop/develop-images/multistage-build/"
|
||||
rel="nofollow"
|
||||
>multi-stage build</a
|
||||
>
|
||||
to keep my image size small. First stage is
|
||||
<a href="https://hub.docker.com/_/golang" rel="nofollow"
|
||||
>Go official image</a
|
||||
>, second stage is
|
||||
<a href="https://github.com/GoogleContainerTools/distroless">Distroless</a
|
||||
>.
|
||||
</p>
|
||||
<p>
|
||||
Before Distroless, I use
|
||||
<a href="https://hub.docker.com/_/alpine" rel="nofollow"
|
||||
>Alpine official image</a
|
||||
>, There is a whole discussion on the Internet to choose which is the best
|
||||
base image for Go. After reading some blogs, I discover Distroless as a
|
||||
small and secure base image. So I stick with it for a while.
|
||||
</p>
|
||||
<p>
|
||||
Also, remember to match Distroless Debian version with Go official image
|
||||
Debian version.
|
||||
</p>
|
||||
<div class="highlight highlight-source-dockerfile">
|
||||
<pre><span class="pl-k">FROM</span> golang:1.19-bullseye as builder</pre>
|
||||
</div>
|
||||
<p>
|
||||
This is Go image I use as a build stage. This can be official Go image or
|
||||
custom image is required in some companies.
|
||||
</p>
|
||||
<div class="highlight highlight-source-dockerfile">
|
||||
<pre><span class="pl-k">RUN</span> go install golang.org/dl/go1.19@latest \
|
||||
&& go1.19 download</pre>
|
||||
</div>
|
||||
<p>
|
||||
This is optional. In my case, my company is slow to update Go image so I
|
||||
use this trick to install latest Go version.
|
||||
</p>
|
||||
<div class="highlight highlight-source-dockerfile">
|
||||
<pre><span class="pl-k">WORKDIR</span> /build
|
||||
|
||||
<span class="pl-k">COPY</span> go.mod .
|
||||
<span class="pl-k">COPY</span> go.sum .
|
||||
<span class="pl-k">COPY</span> vendor .
|
||||
<span class="pl-k">COPY</span> . .</pre></div>
|
||||
<p>I use <code>/build</code> to emphasize that I am building something in that directory.</p>
|
||||
<p>The 4 <code>COPY</code> lines are familiar if you use Go enough.
|
||||
First is <code>go.mod</code> and <code>go.sum</code> because it defines Go modules.
|
||||
The second is <code>vendor</code>, this is optional but I use it because I don't want each time I build Dockerfile, I need to redownload Go modules.</p>
|
||||
<div class="highlight highlight-source-dockerfile"><pre><span class="pl-k">RUN</span> CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOAMD64=v3 go build -o ./app -tags timetzdata -trimpath .</pre></div>
|
||||
<p>This is where I build Go program.</p>
|
||||
<p><code>CGO_ENABLED=0</code> because I don't want to mess with C libraries.
|
||||
<code>GOOS=linux GOARCH=amd64</code> is easy to explain, Linux with x86-64.
|
||||
<code>GOAMD64=v3</code> is new since <a href="https://go.dev/doc/go1.18#amd64" rel="nofollow">Go 1.18</a>,
|
||||
I use v3 because I read about AMD64 version in <a href="https://gitlab.archlinux.org/archlinux/rfcs/-/blob/master/rfcs/0002-march.rst" rel="nofollow">Arch Linux rfcs</a>. TLDR's newer computers are already x86-64-v3.</p>
|
||||
<p><code>-tags timetzdata</code> to embed timezone database incase base image does not have.
|
||||
<code>-trimpath</code> to support reproduce build.</p>
|
||||
<div class="highlight highlight-source-dockerfile"><pre><span class="pl-k">FROM</span> gcr.io/distroless/base-debian11
|
||||
<span class="pl-k">COPY</span> . .</pre>
|
||||
</div>
|
||||
<p>
|
||||
I use <code>/build</code> to emphasize that I am building something in
|
||||
that directory.
|
||||
</p>
|
||||
<p>
|
||||
The 4 <code>COPY</code> lines are familiar if you use Go enough. First is
|
||||
<code>go.mod</code> and <code>go.sum</code> because it defines Go modules.
|
||||
The second is <code>vendor</code>, this is optional but I use it because I
|
||||
don't want each time I build Dockerfile, I need to redownload Go modules.
|
||||
</p>
|
||||
<div class="highlight highlight-source-dockerfile">
|
||||
<pre><span class="pl-k">RUN</span> CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOAMD64=v3 go build -o ./app -tags timetzdata -trimpath .</pre>
|
||||
</div>
|
||||
<p>This is where I build Go program.</p>
|
||||
<p>
|
||||
<code>CGO_ENABLED=0</code> because I don't want to mess with C libraries.
|
||||
<code>GOOS=linux GOARCH=amd64</code> is easy to explain, Linux with
|
||||
x86-64. <code>GOAMD64=v3</code> is new since
|
||||
<a href="https://go.dev/doc/go1.18#amd64" rel="nofollow">Go 1.18</a>, I
|
||||
use v3 because I read about AMD64 version in
|
||||
<a
|
||||
href="https://gitlab.archlinux.org/archlinux/rfcs/-/blob/master/rfcs/0002-march.rst"
|
||||
rel="nofollow"
|
||||
>Arch Linux rfcs</a
|
||||
>. TLDR's newer computers are already x86-64-v3.
|
||||
</p>
|
||||
<p>
|
||||
<code>-tags timetzdata</code> to embed timezone database incase base image
|
||||
does not have. <code>-trimpath</code> to support reproduce build.
|
||||
</p>
|
||||
<div class="highlight highlight-source-dockerfile">
|
||||
<pre><span class="pl-k">FROM</span> gcr.io/distroless/base-debian11
|
||||
|
||||
<span class="pl-k">COPY</span> --from=builder /build/app /app
|
||||
|
||||
<span class="pl-k">ENTRYPOINT</span> [<span class="pl-s">"/app"</span>]</pre></div>
|
||||
<p>Finally, I copy <code>app</code> to Distroless base image.</p>
|
||||
<span class="pl-k">ENTRYPOINT</span> [<span class="pl-s">"/app"</span>]</pre>
|
||||
</div>
|
||||
<p>Finally, I copy <code>app</code> to Distroless base image.</p>
|
||||
|
||||
<div>
|
||||
Feel free to ask me via
|
||||
|
|
|
@ -43,13 +43,32 @@
|
|||
<div>
|
||||
<a href="index">Index</a>
|
||||
</div>
|
||||
<h1><a id="user-content-bootstrap-go" class="anchor" aria-hidden="true" href="#bootstrap-go"><span aria-hidden="true" class="octicon octicon-link"></span></a>Bootstrap Go</h1>
|
||||
<p>It is hard to write bootstrap tool to quickly create Go service.
|
||||
So I write this guide instead.
|
||||
This is a quick checklist for me every damn time I need to write a Go service from scratch.
|
||||
Also, this is my personal opinion, so feel free to comment.</p>
|
||||
<h2><a id="user-content-structure" class="anchor" aria-hidden="true" href="#structure"><span aria-hidden="true" class="octicon octicon-link"></span></a>Structure</h2>
|
||||
<div class="highlight highlight-text-adblock"><pre>main.go
|
||||
<h1>
|
||||
<a
|
||||
id="user-content-bootstrap-go"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#bootstrap-go"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Bootstrap Go
|
||||
</h1>
|
||||
<p>
|
||||
It is hard to write bootstrap tool to quickly create Go service. So I
|
||||
write this guide instead. This is a quick checklist for me every damn time
|
||||
I need to write a Go service from scratch. Also, this is my personal
|
||||
opinion, so feel free to comment.
|
||||
</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-structure"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#structure"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Structure
|
||||
</h2>
|
||||
<div class="highlight highlight-text-adblock">
|
||||
<pre>main.go
|
||||
internal
|
||||
<span class="pl-k">|</span> business
|
||||
<span class="pl-k">|</span> | http
|
||||
|
@ -65,57 +84,132 @@ internal
|
|||
<span class="pl-k">|</span> | | models.go
|
||||
<span class="pl-k">|</span> | service.go
|
||||
<span class="pl-k">|</span> | repository.go
|
||||
<span class="pl-k">|</span> | models.go</pre></div>
|
||||
<p>All business codes are inside <code>internal</code>.
|
||||
Each business has a different directory <code>business</code>.</p>
|
||||
<p>Inside each business, there are 2 handlers: <code>http</code>, <code>grpc</code>:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>http</code> is for public APIs (Android, iOS, ... are clients).</li>
|
||||
<li>
|
||||
<code>grpc</code> is for internal APIs (other services are clients).</li>
|
||||
<li>
|
||||
<code>consumer</code> is for consuming messages from queue (Kafka, RabbitMQ, ...).</li>
|
||||
</ul>
|
||||
<p>For each handler, there are usually 3 layers: <code>handler</code>, <code>service</code>, <code>repository</code>:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>handler</code> interacts directly with gRPC, REST or consumer using specific codes (cookies, ...) In case gRPC, there are frameworks outside handle for us so we can write business/logic codes here too. But remember, gRPC only.</li>
|
||||
<li>
|
||||
<code>service</code> is where we write business/logic codes, and only business/logic codes is written here.</li>
|
||||
<li>
|
||||
<code>repository</code> is where we write codes which interacts with database/cache like MySQL, Redis, ...</li>
|
||||
<li>
|
||||
<code>models</code> is where we put all request, response, data models.</li>
|
||||
</ul>
|
||||
<p>Location:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>handler</code> must exist inside <code>grpc</code>, <code>http</code>, <code>consumer</code>.</li>
|
||||
<li>
|
||||
<code>service</code>, <code>models</code> can exist directly inside of <code>business</code> if both <code>grpc</code>, <code>http</code>, <code>consumer</code> has same business/logic.</li>
|
||||
<li>
|
||||
<code>repository</code> should be placed directly inside of <code>business</code>.</li>
|
||||
</ul>
|
||||
<h2><a id="user-content-do-not-repeat" class="anchor" aria-hidden="true" href="#do-not-repeat"><span aria-hidden="true" class="octicon octicon-link"></span></a>Do not repeat!</h2>
|
||||
<p>If we have too many services, some of the logic will be overlapped.</p>
|
||||
<p>For example, service A and service B both need to make POST call API to service C.
|
||||
If service A and service B both have libs to call service C to do that API, we need to move the libs to some common pkg libs.
|
||||
So in the future, service D which needs to call C will not need to copy libs to handle service C api but only need to import from common pkg libs.</p>
|
||||
<p>Another bad practice is adapter service.
|
||||
No need to write a new service if what we need is just common pkg libs.</p>
|
||||
<h2><a id="user-content-taste-on-style-guide" class="anchor" aria-hidden="true" href="#taste-on-style-guide"><span aria-hidden="true" class="octicon octicon-link"></span></a>Taste on style guide</h2>
|
||||
<h3><a id="user-content-stop-using-global-var" class="anchor" aria-hidden="true" href="#stop-using-global-var"><span aria-hidden="true" class="octicon octicon-link"></span></a>Stop using global var</h3>
|
||||
<p>If I see someone using global var, I swear I will shoot them twice in the face.</p>
|
||||
<p>Why?</p>
|
||||
<ul>
|
||||
<li>Can not write unit test.</li>
|
||||
<li>Is not thread safe.</li>
|
||||
</ul>
|
||||
<h3><a id="user-content-use-functional-options-but-dont-overuse-it" class="anchor" aria-hidden="true" href="#use-functional-options-but-dont-overuse-it"><span aria-hidden="true" class="octicon octicon-link"></span></a>Use functional options, but don't overuse it!</h3>
|
||||
<p>For simple struct with 1 or 2 fields, no need to use functional options.</p>
|
||||
<p><a href="https://go.dev/play/p/0XnOLiHuoz3" rel="nofollow">Example</a>:</p>
|
||||
<div class="highlight highlight-source-go"><pre><span class="pl-k">func</span> <span class="pl-en">main</span>() {
|
||||
<span class="pl-k">|</span> | models.go</pre>
|
||||
</div>
|
||||
<p>
|
||||
All business codes are inside <code>internal</code>. Each business has a
|
||||
different directory <code>business</code>.
|
||||
</p>
|
||||
<p>
|
||||
Inside each business, there are 2 handlers: <code>http</code>,
|
||||
<code>grpc</code>:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>http</code> is for public APIs (Android, iOS, ... are clients).
|
||||
</li>
|
||||
<li>
|
||||
<code>grpc</code> is for internal APIs (other services are clients).
|
||||
</li>
|
||||
<li>
|
||||
<code>consumer</code> is for consuming messages from queue (Kafka,
|
||||
RabbitMQ, ...).
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
For each handler, there are usually 3 layers: <code>handler</code>,
|
||||
<code>service</code>, <code>repository</code>:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>handler</code> interacts directly with gRPC, REST or consumer
|
||||
using specific codes (cookies, ...) In case gRPC, there are frameworks
|
||||
outside handle for us so we can write business/logic codes here too. But
|
||||
remember, gRPC only.
|
||||
</li>
|
||||
<li>
|
||||
<code>service</code> is where we write business/logic codes, and only
|
||||
business/logic codes is written here.
|
||||
</li>
|
||||
<li>
|
||||
<code>repository</code> is where we write codes which interacts with
|
||||
database/cache like MySQL, Redis, ...
|
||||
</li>
|
||||
<li>
|
||||
<code>models</code> is where we put all request, response, data models.
|
||||
</li>
|
||||
</ul>
|
||||
<p>Location:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>handler</code> must exist inside <code>grpc</code>,
|
||||
<code>http</code>, <code>consumer</code>.
|
||||
</li>
|
||||
<li>
|
||||
<code>service</code>, <code>models</code> can exist directly inside of
|
||||
<code>business</code> if both <code>grpc</code>, <code>http</code>,
|
||||
<code>consumer</code> has same business/logic.
|
||||
</li>
|
||||
<li>
|
||||
<code>repository</code> should be placed directly inside of
|
||||
<code>business</code>.
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-do-not-repeat"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#do-not-repeat"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Do not repeat!
|
||||
</h2>
|
||||
<p>If we have too many services, some of the logic will be overlapped.</p>
|
||||
<p>
|
||||
For example, service A and service B both need to make POST call API to
|
||||
service C. If service A and service B both have libs to call service C to
|
||||
do that API, we need to move the libs to some common pkg libs. So in the
|
||||
future, service D which needs to call C will not need to copy libs to
|
||||
handle service C api but only need to import from common pkg libs.
|
||||
</p>
|
||||
<p>
|
||||
Another bad practice is adapter service. No need to write a new service if
|
||||
what we need is just common pkg libs.
|
||||
</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-taste-on-style-guide"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#taste-on-style-guide"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Taste on style guide
|
||||
</h2>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-stop-using-global-var"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#stop-using-global-var"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Stop using global var
|
||||
</h3>
|
||||
<p>
|
||||
If I see someone using global var, I swear I will shoot them twice in the
|
||||
face.
|
||||
</p>
|
||||
<p>Why?</p>
|
||||
<ul>
|
||||
<li>Can not write unit test.</li>
|
||||
<li>Is not thread safe.</li>
|
||||
</ul>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-use-functional-options-but-dont-overuse-it"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#use-functional-options-but-dont-overuse-it"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Use functional options, but don't overuse it!
|
||||
</h3>
|
||||
<p>
|
||||
For simple struct with 1 or 2 fields, no need to use functional options.
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://go.dev/play/p/0XnOLiHuoz3" rel="nofollow">Example</a>:
|
||||
</p>
|
||||
<div class="highlight highlight-source-go">
|
||||
<pre><span class="pl-k">func</span> <span class="pl-en">main</span>() {
|
||||
<span class="pl-s1">s</span> <span class="pl-c1">:=</span> <span class="pl-en">NewS</span>(<span class="pl-en">WithA</span>(<span class="pl-c1">1</span>), <span class="pl-en">WithB</span>(<span class="pl-s">"b"</span>))
|
||||
<span class="pl-s1">fmt</span>.<span class="pl-en">Printf</span>(<span class="pl-s">"%+v<span class="pl-cce">\n</span>"</span>, <span class="pl-s1">s</span>)
|
||||
}
|
||||
|
@ -145,16 +239,40 @@ No need to write a new service if what we need is just common pkg libs.</p>
|
|||
<span class="pl-en">opt</span>(<span class="pl-s1">s</span>)
|
||||
}
|
||||
<span class="pl-k">return</span> <span class="pl-s1">s</span>
|
||||
}</pre></div>
|
||||
<p>In above example, I construct <code>s</code> with <code>WithA</code> and <code>WithB</code> option.
|
||||
No need to pass direct field inside <code>s</code>.</p>
|
||||
<h3><a id="user-content-use-errgroup-as-much-as-possible" class="anchor" aria-hidden="true" href="#use-errgroup-as-much-as-possible"><span aria-hidden="true" class="octicon octicon-link"></span></a>Use <a href="https://pkg.go.dev/golang.org/x/sync/errgroup" rel="nofollow">errgroup</a> as much as possible</h3>
|
||||
<p>If business logic involves calling too many APIs, but they are not depend on each other.
|
||||
We can fire them parallel :)</p>
|
||||
<p>Personally, I prefer <code>errgroup</code> to <code>WaitGroup</code> (<a href="https://pkg.go.dev/sync#WaitGroup" rel="nofollow">https://pkg.go.dev/sync#WaitGroup</a>).
|
||||
Because I always need deal with error.</p>
|
||||
<p>Example:</p>
|
||||
<div class="highlight highlight-source-go"><pre><span class="pl-s1">eg</span>, <span class="pl-s1">egCtx</span> <span class="pl-c1">:=</span> <span class="pl-s1">errgroup</span>.<span class="pl-en">WithContext</span>(<span class="pl-s1">ctx</span>)
|
||||
}</pre>
|
||||
</div>
|
||||
<p>
|
||||
In above example, I construct <code>s</code> with <code>WithA</code> and
|
||||
<code>WithB</code> option. No need to pass direct field inside
|
||||
<code>s</code>.
|
||||
</p>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-use-errgroup-as-much-as-possible"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#use-errgroup-as-much-as-possible"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Use
|
||||
<a href="https://pkg.go.dev/golang.org/x/sync/errgroup" rel="nofollow"
|
||||
>errgroup</a
|
||||
>
|
||||
as much as possible
|
||||
</h3>
|
||||
<p>
|
||||
If business logic involves calling too many APIs, but they are not depend
|
||||
on each other. We can fire them parallel :)
|
||||
</p>
|
||||
<p>
|
||||
Personally, I prefer <code>errgroup</code> to <code>WaitGroup</code> (<a
|
||||
href="https://pkg.go.dev/sync#WaitGroup"
|
||||
rel="nofollow"
|
||||
>https://pkg.go.dev/sync#WaitGroup</a
|
||||
>). Because I always need deal with error.
|
||||
</p>
|
||||
<p>Example:</p>
|
||||
<div class="highlight highlight-source-go">
|
||||
<pre><span class="pl-s1">eg</span>, <span class="pl-s1">egCtx</span> <span class="pl-c1">:=</span> <span class="pl-s1">errgroup</span>.<span class="pl-en">WithContext</span>(<span class="pl-s1">ctx</span>)
|
||||
|
||||
<span class="pl-s1">eg</span>.<span class="pl-en">Go</span>(<span class="pl-k">func</span>() <span class="pl-smi">error</span> {
|
||||
<span class="pl-c">// Do some thing</span>
|
||||
|
@ -168,125 +286,396 @@ Because I always need deal with error.</p>
|
|||
|
||||
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">eg</span>.<span class="pl-en">Wait</span>(); <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
|
||||
<span class="pl-c">// Handle error</span>
|
||||
}</pre></div>
|
||||
<h3><a id="user-content-use-semaphore-when-need-to-implement-workerpool" class="anchor" aria-hidden="true" href="#use-semaphore-when-need-to-implement-workerpool"><span aria-hidden="true" class="octicon octicon-link"></span></a>Use <a href="https://pkg.go.dev/golang.org/x/sync/semaphore" rel="nofollow">semaphore</a> when need to implement WorkerPool</h3>
|
||||
<p>Please don't use external libs for WorkerPool, I don't want to deal with dependency hell.</p>
|
||||
<h2><a id="user-content-external-libs" class="anchor" aria-hidden="true" href="#external-libs"><span aria-hidden="true" class="octicon octicon-link"></span></a>External libs</h2>
|
||||
<h3><a id="user-content-no-need-vendor" class="anchor" aria-hidden="true" href="#no-need-vendor"><span aria-hidden="true" class="octicon octicon-link"></span></a>No need <code>vendor</code>
|
||||
</h3>
|
||||
<p>Only need if you need something from <code>vendor</code>, to generate mock or something else.</p>
|
||||
<h3><a id="user-content-use-buildgo-to-include-build-tools-in-gomod" class="anchor" aria-hidden="true" href="#use-buildgo-to-include-build-tools-in-gomod"><span aria-hidden="true" class="octicon octicon-link"></span></a>Use <code>build.go</code> to include build tools in go.mod</h3>
|
||||
<p>To easily control version of build tools.</p>
|
||||
<p>For example <code>build.go</code>:</p>
|
||||
<div class="highlight highlight-source-go"><pre><span class="pl-c">//go:build tools</span>
|
||||
}</pre>
|
||||
</div>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-use-semaphore-when-need-to-implement-workerpool"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#use-semaphore-when-need-to-implement-workerpool"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Use
|
||||
<a href="https://pkg.go.dev/golang.org/x/sync/semaphore" rel="nofollow"
|
||||
>semaphore</a
|
||||
>
|
||||
when need to implement WorkerPool
|
||||
</h3>
|
||||
<p>
|
||||
Please don't use external libs for WorkerPool, I don't want to deal with
|
||||
dependency hell.
|
||||
</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-external-libs"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#external-libs"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>External libs
|
||||
</h2>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-no-need-vendor"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#no-need-vendor"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>No need <code>vendor</code>
|
||||
</h3>
|
||||
<p>
|
||||
Only need if you need something from <code>vendor</code>, to generate mock
|
||||
or something else.
|
||||
</p>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-use-buildgo-to-include-build-tools-in-gomod"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#use-buildgo-to-include-build-tools-in-gomod"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Use <code>build.go</code> to include build tools in go.mod
|
||||
</h3>
|
||||
<p>To easily control version of build tools.</p>
|
||||
<p>For example <code>build.go</code>:</p>
|
||||
<div class="highlight highlight-source-go">
|
||||
<pre><span class="pl-c">//go:build tools</span>
|
||||
<span class="pl-c">// +build tools</span>
|
||||
|
||||
<span class="pl-k">package</span> main
|
||||
|
||||
<span class="pl-k">import</span> (
|
||||
_ <span class="pl-s">"github.com/golang/protobuf/protoc-gen-go"</span>
|
||||
)</pre></div>
|
||||
<p>And then in <code>Makefile</code>:</p>
|
||||
<div class="highlight highlight-source-makefile"><pre><span class="pl-en">build</span>:
|
||||
go install github.com/golang/protobuf/protoc-gen-go</pre></div>
|
||||
<p>We always get the version of build tools in <code>go.mod</code> each time we install it.
|
||||
Future contributors will not cry anymore.</p>
|
||||
<h3><a id="user-content-dont-use-cli-libs-spf13cobra-urfavecli-just-for-go-service" class="anchor" aria-hidden="true" href="#dont-use-cli-libs-spf13cobra-urfavecli-just-for-go-service"><span aria-hidden="true" class="octicon octicon-link"></span></a>Don't use cli libs (<a href="https://github.com/spf13/cobra">spf13/cobra</a>, <a href="https://github.com/urfave/cli">urfave/cli</a>) just for Go service</h3>
|
||||
<p>What is the point to pass many params (<code>do-it</code>, <code>--abc</code>, <code>--xyz</code>) when what we only need is start service?</p>
|
||||
<p>In my case, service starts with only config, and config should be read from file or environment like <a href="https://12factor.net/" rel="nofollow">The Twelve Factors</a> guide.</p>
|
||||
<h3><a id="user-content-dont-use-grpc-ecosystemgrpc-gateway" class="anchor" aria-hidden="true" href="#dont-use-grpc-ecosystemgrpc-gateway"><span aria-hidden="true" class="octicon octicon-link"></span></a>Don't use <a href="https://github.com/grpc-ecosystem/grpc-gateway">grpc-ecosystem/grpc-gateway</a>
|
||||
</h3>
|
||||
<p>Just don't.</p>
|
||||
<p>Use <a href="https://github.com/protocolbuffers/protobuf-go">protocolbuffers/protobuf-go</a>, <a href="https://github.com/grpc/grpc-go">grpc/grpc-go</a> for gRPC.</p>
|
||||
<p>Write 1 for both gRPC, REST sounds good, but in the end, it is not worth it.</p>
|
||||
<h3><a id="user-content-dont-use-uberprototool-use-bufbuildbuf" class="anchor" aria-hidden="true" href="#dont-use-uberprototool-use-bufbuildbuf"><span aria-hidden="true" class="octicon octicon-link"></span></a>Don't use <a href="https://github.com/uber/prototool">uber/prototool</a>, use <a href="https://github.com/bufbuild/buf">bufbuild/buf</a>
|
||||
</h3>
|
||||
<p>prototool is deprecated, and buf can generate, lint, format as good as prototool.</p>
|
||||
<h3><a id="user-content-use-gin-gonicgin-for-rest" class="anchor" aria-hidden="true" href="#use-gin-gonicgin-for-rest"><span aria-hidden="true" class="octicon octicon-link"></span></a>Use <a href="https://github.com/gin-gonic/gin">gin-gonic/gin</a> for REST.</h3>
|
||||
<p>Don't use <code>gin.Context</code> when pass context from handler layer to service layer, use <code>gin.Context.Request.Context()</code> instead.</p>
|
||||
<h3><a id="user-content-if-you-want-log-just-use-uber-gozap" class="anchor" aria-hidden="true" href="#if-you-want-log-just-use-uber-gozap"><span aria-hidden="true" class="octicon octicon-link"></span></a>If you want log, just use <a href="https://github.com/uber-go/zap">uber-go/zap</a>
|
||||
</h3>
|
||||
<p>It is fast!</p>
|
||||
<ul>
|
||||
<li>Don't overuse <code>func (*Logger) With</code>. Because if log line is too long, there is a possibility that we can lost it.</li>
|
||||
<li>Use <code>MarshalLogObject</code> when we need to hide some field of object when log (field is long or has sensitive value)</li>
|
||||
<li>Don't use <code>Panic</code>. Use <code>Fatal</code> for errors when start service to check dependencies. If you really need panic level, use <code>DPanic</code>.</li>
|
||||
<li>If doubt, use <code>zap.Any</code>.</li>
|
||||
<li>Use <code>contextID</code> or <code>traceID</code> in every log lines for easily debug.</li>
|
||||
</ul>
|
||||
<h3><a id="user-content-to-read-config-use-spf13viper" class="anchor" aria-hidden="true" href="#to-read-config-use-spf13viper"><span aria-hidden="true" class="octicon octicon-link"></span></a>To read config, use <a href="https://github.com/spf13/viper">spf13/viper</a>
|
||||
</h3>
|
||||
<p>Only init config in main or cmd layer.
|
||||
Do not use <code>viper.Get...</code> in business layer or inside business layer.</p>
|
||||
<p>Why?</p>
|
||||
<ul>
|
||||
<li>Hard to mock and test</li>
|
||||
<li>Put all config in single place for easily tracking</li>
|
||||
</ul>
|
||||
<p>Also, be careful if config value is empty.
|
||||
You should decide to continue or stop the service if there is no config.</p>
|
||||
<h3><a id="user-content-dont-overuse-orm-libs-no-need-to-handle-another-layer-above-sql" class="anchor" aria-hidden="true" href="#dont-overuse-orm-libs-no-need-to-handle-another-layer-above-sql"><span aria-hidden="true" class="octicon octicon-link"></span></a>Don't overuse ORM libs, no need to handle another layer above SQL.</h3>
|
||||
<p>Each ORM libs has each different syntax.
|
||||
To learn and use those libs correctly is time consuming.
|
||||
So just stick to plain SQL.
|
||||
It is easier to debug when something is wrong.</p>
|
||||
<p>But <code>database/sql</code> has its own limit.
|
||||
For example, it is hard to get primary key after insert/update.
|
||||
So may be you want to use ORM for those cases.
|
||||
I hear that <a href="https://github.com/go-gorm/gorm">go-gorm/gorm</a>, <a href="https://github.com/ent/ent">ent/ent</a> is good.</p>
|
||||
<h3><a id="user-content-if-you-want-test-just-use-stretchrtestify" class="anchor" aria-hidden="true" href="#if-you-want-test-just-use-stretchrtestify"><span aria-hidden="true" class="octicon octicon-link"></span></a>If you want test, just use <a href="https://github.com/stretchr/testify">stretchr/testify</a>.</h3>
|
||||
<p>It is easy to write a suite test, thanks to testify.
|
||||
Also, for mocking, there are many options out there.
|
||||
Pick 1 then sleep peacefully.</p>
|
||||
<h3><a id="user-content-if-need-to-mock-choose-matryermoq-or-golangmock" class="anchor" aria-hidden="true" href="#if-need-to-mock-choose-matryermoq-or-golangmock"><span aria-hidden="true" class="octicon octicon-link"></span></a>If need to mock, choose <a href="https://github.com/matryer/moq">matryer/moq</a> or <a href="https://github.com/golang/mock">golang/mock</a>
|
||||
</h3>
|
||||
<p>The first is easy to use but not powerful as the later.
|
||||
If you want to make sure mock func is called with correct times, use the later.</p>
|
||||
<p>Example with <code>matryer/moq</code>:</p>
|
||||
<div class="highlight highlight-source-go"><pre><span class="pl-c">// Only gen mock if source code file is newer than mock file</span>
|
||||
)</pre>
|
||||
</div>
|
||||
<p>And then in <code>Makefile</code>:</p>
|
||||
<div class="highlight highlight-source-makefile">
|
||||
<pre><span class="pl-en">build</span>:
|
||||
go install github.com/golang/protobuf/protoc-gen-go</pre>
|
||||
</div>
|
||||
<p>
|
||||
We always get the version of build tools in <code>go.mod</code> each time
|
||||
we install it. Future contributors will not cry anymore.
|
||||
</p>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-dont-use-cli-libs-spf13cobra-urfavecli-just-for-go-service"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#dont-use-cli-libs-spf13cobra-urfavecli-just-for-go-service"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Don't use cli libs (<a href="https://github.com/spf13/cobra"
|
||||
>spf13/cobra</a
|
||||
>, <a href="https://github.com/urfave/cli">urfave/cli</a>) just for Go
|
||||
service
|
||||
</h3>
|
||||
<p>
|
||||
What is the point to pass many params (<code>do-it</code>,
|
||||
<code>--abc</code>, <code>--xyz</code>) when what we only need is start
|
||||
service?
|
||||
</p>
|
||||
<p>
|
||||
In my case, service starts with only config, and config should be read
|
||||
from file or environment like
|
||||
<a href="https://12factor.net/" rel="nofollow">The Twelve Factors</a>
|
||||
guide.
|
||||
</p>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-dont-use-grpc-ecosystemgrpc-gateway"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#dont-use-grpc-ecosystemgrpc-gateway"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Don't use
|
||||
<a href="https://github.com/grpc-ecosystem/grpc-gateway"
|
||||
>grpc-ecosystem/grpc-gateway</a
|
||||
>
|
||||
</h3>
|
||||
<p>Just don't.</p>
|
||||
<p>
|
||||
Use
|
||||
<a href="https://github.com/protocolbuffers/protobuf-go"
|
||||
>protocolbuffers/protobuf-go</a
|
||||
>, <a href="https://github.com/grpc/grpc-go">grpc/grpc-go</a> for gRPC.
|
||||
</p>
|
||||
<p>
|
||||
Write 1 for both gRPC, REST sounds good, but in the end, it is not worth
|
||||
it.
|
||||
</p>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-dont-use-uberprototool-use-bufbuildbuf"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#dont-use-uberprototool-use-bufbuildbuf"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Don't use <a href="https://github.com/uber/prototool">uber/prototool</a>,
|
||||
use <a href="https://github.com/bufbuild/buf">bufbuild/buf</a>
|
||||
</h3>
|
||||
<p>
|
||||
prototool is deprecated, and buf can generate, lint, format as good as
|
||||
prototool.
|
||||
</p>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-use-gin-gonicgin-for-rest"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#use-gin-gonicgin-for-rest"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Use <a href="https://github.com/gin-gonic/gin">gin-gonic/gin</a> for
|
||||
REST.
|
||||
</h3>
|
||||
<p>
|
||||
Don't use <code>gin.Context</code> when pass context from handler layer to
|
||||
service layer, use <code>gin.Context.Request.Context()</code> instead.
|
||||
</p>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-if-you-want-log-just-use-uber-gozap"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#if-you-want-log-just-use-uber-gozap"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>If you want log, just use
|
||||
<a href="https://github.com/uber-go/zap">uber-go/zap</a>
|
||||
</h3>
|
||||
<p>It is fast!</p>
|
||||
<ul>
|
||||
<li>
|
||||
Don't overuse <code>func (*Logger) With</code>. Because if log line is
|
||||
too long, there is a possibility that we can lost it.
|
||||
</li>
|
||||
<li>
|
||||
Use <code>MarshalLogObject</code> when we need to hide some field of
|
||||
object when log (field is long or has sensitive value)
|
||||
</li>
|
||||
<li>
|
||||
Don't use <code>Panic</code>. Use <code>Fatal</code> for errors when
|
||||
start service to check dependencies. If you really need panic level, use
|
||||
<code>DPanic</code>.
|
||||
</li>
|
||||
<li>If doubt, use <code>zap.Any</code>.</li>
|
||||
<li>
|
||||
Use <code>contextID</code> or <code>traceID</code> in every log lines
|
||||
for easily debug.
|
||||
</li>
|
||||
</ul>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-to-read-config-use-spf13viper"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#to-read-config-use-spf13viper"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>To read config, use
|
||||
<a href="https://github.com/spf13/viper">spf13/viper</a>
|
||||
</h3>
|
||||
<p>
|
||||
Only init config in main or cmd layer. Do not use
|
||||
<code>viper.Get...</code> in business layer or inside business layer.
|
||||
</p>
|
||||
<p>Why?</p>
|
||||
<ul>
|
||||
<li>Hard to mock and test</li>
|
||||
<li>Put all config in single place for easily tracking</li>
|
||||
</ul>
|
||||
<p>
|
||||
Also, be careful if config value is empty. You should decide to continue
|
||||
or stop the service if there is no config.
|
||||
</p>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-dont-overuse-orm-libs-no-need-to-handle-another-layer-above-sql"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#dont-overuse-orm-libs-no-need-to-handle-another-layer-above-sql"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Don't overuse ORM libs, no need to handle another layer above SQL.
|
||||
</h3>
|
||||
<p>
|
||||
Each ORM libs has each different syntax. To learn and use those libs
|
||||
correctly is time consuming. So just stick to plain SQL. It is easier to
|
||||
debug when something is wrong.
|
||||
</p>
|
||||
<p>
|
||||
But <code>database/sql</code> has its own limit. For example, it is hard
|
||||
to get primary key after insert/update. So may be you want to use ORM for
|
||||
those cases. I hear that
|
||||
<a href="https://github.com/go-gorm/gorm">go-gorm/gorm</a>,
|
||||
<a href="https://github.com/ent/ent">ent/ent</a> is good.
|
||||
</p>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-if-you-want-test-just-use-stretchrtestify"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#if-you-want-test-just-use-stretchrtestify"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>If you want test, just use
|
||||
<a href="https://github.com/stretchr/testify">stretchr/testify</a>.
|
||||
</h3>
|
||||
<p>
|
||||
It is easy to write a suite test, thanks to testify. Also, for mocking,
|
||||
there are many options out there. Pick 1 then sleep peacefully.
|
||||
</p>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-if-need-to-mock-choose-matryermoq-or-golangmock"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#if-need-to-mock-choose-matryermoq-or-golangmock"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>If need to mock, choose
|
||||
<a href="https://github.com/matryer/moq">matryer/moq</a> or
|
||||
<a href="https://github.com/golang/mock">golang/mock</a>
|
||||
</h3>
|
||||
<p>
|
||||
The first is easy to use but not powerful as the later. If you want to
|
||||
make sure mock func is called with correct times, use the later.
|
||||
</p>
|
||||
<p>Example with <code>matryer/moq</code>:</p>
|
||||
<div class="highlight highlight-source-go">
|
||||
<pre><span class="pl-c">// Only gen mock if source code file is newer than mock file</span>
|
||||
<span class="pl-c">// https://jonwillia.ms/2019/12/22/conditional-gomock-mockgen</span>
|
||||
<span class="pl-c">//go:generate sh -c "test service_mock_generated.go -nt $GOFILE && exit 0; moq -rm -out service_mock_generated.go . Service"</span></pre></div>
|
||||
<h3><a id="user-content-be-careful-with-spf13cast" class="anchor" aria-hidden="true" href="#be-careful-with-spf13cast"><span aria-hidden="true" class="octicon octicon-link"></span></a>Be careful with <a href="https://github.com/spf13/cast">spf13/cast</a>
|
||||
</h3>
|
||||
<p>Don't cast proto enum:</p>
|
||||
<div class="highlight highlight-source-go"><pre><span class="pl-c">// Bad</span>
|
||||
<span class="pl-c">//go:generate sh -c "test service_mock_generated.go -nt $GOFILE && exit 0; moq -rm -out service_mock_generated.go . Service"</span></pre>
|
||||
</div>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-be-careful-with-spf13cast"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#be-careful-with-spf13cast"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Be careful with <a href="https://github.com/spf13/cast">spf13/cast</a>
|
||||
</h3>
|
||||
<p>Don't cast proto enum:</p>
|
||||
<div class="highlight highlight-source-go">
|
||||
<pre><span class="pl-c">// Bad</span>
|
||||
<span class="pl-s1">a</span> <span class="pl-c1">:=</span> <span class="pl-s1">cast</span>.<span class="pl-en">ToInt32</span>(<span class="pl-s1">servicev1</span>.<span class="pl-c1">ReasonCode_ABC</span>)
|
||||
|
||||
<span class="pl-c">// Good</span>
|
||||
<span class="pl-s1">a</span> <span class="pl-c1">:=</span> <span class="pl-en">int32</span>(<span class="pl-s1">servicev1</span>.<span class="pl-c1">ReasonCode_ABC</span>)</pre></div>
|
||||
<h3><a id="user-content-use-stringer-if-you-want-your-type-enum-can-be-print-as-string" class="anchor" aria-hidden="true" href="#use-stringer-if-you-want-your-type-enum-can-be-print-as-string"><span aria-hidden="true" class="octicon octicon-link"></span></a>Use <a href="https://pkg.go.dev/golang.org/x/tools/cmd/stringer" rel="nofollow">stringer</a> if you want your type enum can be print as string</h3>
|
||||
<div class="highlight highlight-source-go"><pre><span class="pl-k">type</span> <span class="pl-smi">Drink</span> <span class="pl-smi">int</span>
|
||||
<span class="pl-s1">a</span> <span class="pl-c1">:=</span> <span class="pl-en">int32</span>(<span class="pl-s1">servicev1</span>.<span class="pl-c1">ReasonCode_ABC</span>)</pre>
|
||||
</div>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-use-stringer-if-you-want-your-type-enum-can-be-print-as-string"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#use-stringer-if-you-want-your-type-enum-can-be-print-as-string"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Use
|
||||
<a
|
||||
href="https://pkg.go.dev/golang.org/x/tools/cmd/stringer"
|
||||
rel="nofollow"
|
||||
>stringer</a
|
||||
>
|
||||
if you want your type enum can be print as string
|
||||
</h3>
|
||||
<div class="highlight highlight-source-go">
|
||||
<pre><span class="pl-k">type</span> <span class="pl-smi">Drink</span> <span class="pl-smi">int</span>
|
||||
|
||||
<span class="pl-k">const</span> (
|
||||
<span class="pl-s1">Beer</span> <span class="pl-smi">Drink</span> <span class="pl-c1">=</span> <span class="pl-s1">iota</span>
|
||||
<span class="pl-s1">Water</span>
|
||||
<span class="pl-s1">OrangeJuice</span>
|
||||
)</pre></div>
|
||||
<div class="highlight highlight-source-shell"><pre>go install golang.org/x/tools/cmd/stringer@latest
|
||||
)</pre>
|
||||
</div>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre>go install golang.org/x/tools/cmd/stringer@latest
|
||||
|
||||
<span class="pl-c"><span class="pl-c">#</span> Run inside directory which contains Drink</span>
|
||||
stringer -type=Drink</pre></div>
|
||||
<h3><a id="user-content-dont-waste-your-time-rewrite-rate-limiter-if-your-use-case-is-simple-use-rate-or-go-redisredis_rate" class="anchor" aria-hidden="true" href="#dont-waste-your-time-rewrite-rate-limiter-if-your-use-case-is-simple-use-rate-or-go-redisredis_rate"><span aria-hidden="true" class="octicon octicon-link"></span></a>Don't waste your time rewrite rate limiter if your use case is simple, use <a href="https://pkg.go.dev/golang.org/x/time/rate" rel="nofollow">rate</a> or <a href="https://github.com/go-redis/redis_rate">go-redis/redis_rate</a>
|
||||
</h3>
|
||||
<p>rate if you want rate limiter locally in your single instance of service.
|
||||
redis_rate if you want rate limiter distributed across all your instances of service.</p>
|
||||
<h3><a id="user-content-replace-go-fmt-goimports-with-mvdangofumpt" class="anchor" aria-hidden="true" href="#replace-go-fmt-goimports-with-mvdangofumpt"><span aria-hidden="true" class="octicon octicon-link"></span></a>Replace <code>go fmt</code>, <code>goimports</code> with <a href="https://github.com/mvdan/gofumpt">mvdan/gofumpt</a>.</h3>
|
||||
<p><code>gofumpt</code> provides more rules when format Go codes.</p>
|
||||
<h3><a id="user-content-use-golangcigolangci-lint" class="anchor" aria-hidden="true" href="#use-golangcigolangci-lint"><span aria-hidden="true" class="octicon octicon-link"></span></a>Use <a href="https://github.com/golangci/golangci-lint">golangci/golangci-lint</a>.</h3>
|
||||
<p>No need to say more.
|
||||
Lint or get the f out!</p>
|
||||
<p>If you get <code>fieldalignment</code> error, use <a href="https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/fieldalignment" rel="nofollow">fieldalignment</a> to fix them.</p>
|
||||
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#</span> Install</span>
|
||||
stringer -type=Drink</pre>
|
||||
</div>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-dont-waste-your-time-rewrite-rate-limiter-if-your-use-case-is-simple-use-rate-or-go-redisredis_rate"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#dont-waste-your-time-rewrite-rate-limiter-if-your-use-case-is-simple-use-rate-or-go-redisredis_rate"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Don't waste your time rewrite rate limiter if your use case is simple,
|
||||
use
|
||||
<a href="https://pkg.go.dev/golang.org/x/time/rate" rel="nofollow"
|
||||
>rate</a
|
||||
>
|
||||
or
|
||||
<a href="https://github.com/go-redis/redis_rate">go-redis/redis_rate</a>
|
||||
</h3>
|
||||
<p>
|
||||
rate if you want rate limiter locally in your single instance of service.
|
||||
redis_rate if you want rate limiter distributed across all your instances
|
||||
of service.
|
||||
</p>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-replace-go-fmt-goimports-with-mvdangofumpt"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#replace-go-fmt-goimports-with-mvdangofumpt"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Replace <code>go fmt</code>, <code>goimports</code> with
|
||||
<a href="https://github.com/mvdan/gofumpt">mvdan/gofumpt</a>.
|
||||
</h3>
|
||||
<p><code>gofumpt</code> provides more rules when format Go codes.</p>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-use-golangcigolangci-lint"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#use-golangcigolangci-lint"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Use
|
||||
<a href="https://github.com/golangci/golangci-lint"
|
||||
>golangci/golangci-lint</a
|
||||
>.
|
||||
</h3>
|
||||
<p>No need to say more. Lint or get the f out!</p>
|
||||
<p>
|
||||
If you get <code>fieldalignment</code> error, use
|
||||
<a
|
||||
href="https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/fieldalignment"
|
||||
rel="nofollow"
|
||||
>fieldalignment</a
|
||||
>
|
||||
to fix them.
|
||||
</p>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre><span class="pl-c"><span class="pl-c">#</span> Install</span>
|
||||
go install golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment@latest
|
||||
|
||||
<span class="pl-c"><span class="pl-c">#</span> Fix</span>
|
||||
fieldalignment -fix ./internal/business/<span class="pl-k">*</span>.go</pre></div>
|
||||
<h2><a id="user-content-thanks" class="anchor" aria-hidden="true" href="#thanks"><span aria-hidden="true" class="octicon octicon-link"></span></a>Thanks</h2>
|
||||
<ul>
|
||||
<li><a href="https://github.com/uber-go/guide/blob/master/style.md">Uber Go Style Guide</a></li>
|
||||
<li><a href="https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis" rel="nofollow">Functional options for friendly APIs</a></li>
|
||||
<li><a href="https://google.github.io/styleguide/go/index" rel="nofollow">Google Go Style</a></li>
|
||||
</ul>
|
||||
fieldalignment -fix ./internal/business/<span class="pl-k">*</span>.go</pre>
|
||||
</div>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-thanks"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#thanks"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Thanks
|
||||
</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/uber-go/guide/blob/master/style.md"
|
||||
>Uber Go Style Guide</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis"
|
||||
rel="nofollow"
|
||||
>Functional options for friendly APIs</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://google.github.io/styleguide/go/index" rel="nofollow"
|
||||
>Google Go Style</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
Feel free to ask me via
|
||||
|
|
|
@ -43,46 +43,137 @@
|
|||
<div>
|
||||
<a href="index">Index</a>
|
||||
</div>
|
||||
<h1><a id="user-content-uuid-or-else" class="anchor" aria-hidden="true" href="#uuid-or-else"><span aria-hidden="true" class="octicon octicon-link"></span></a>UUID or else</h1>
|
||||
<p>There are many use cases where we need to use a unique ID.
|
||||
In my experience, I only encouter 2 cases:</p>
|
||||
<ul>
|
||||
<li>ID to trace request from client to server, from service to service (microservice architecture or nanoservice I don't know).</li>
|
||||
<li>Primary key for database.</li>
|
||||
</ul>
|
||||
<p>In my Go universe, there are some libs to help us with this:</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/google/uuid">google/uuid</a></li>
|
||||
<li><a href="https://github.com/rs/xid">rs/xid</a></li>
|
||||
<li><a href="https://github.com/segmentio/ksuid">segmentio/ksuid</a></li>
|
||||
<li><a href="https://github.com/oklog/ulid">oklog/ulid</a></li>
|
||||
</ul>
|
||||
<h2><a id="user-content-first-use-case-is-trace-id-or-context-aware-id" class="anchor" aria-hidden="true" href="#first-use-case-is-trace-id-or-context-aware-id"><span aria-hidden="true" class="octicon octicon-link"></span></a>First use case is trace ID, or context aware ID</h2>
|
||||
<p>The ID is used only for trace and log.
|
||||
If same ID is generated twice (because maybe the possibilty is too small but not 0), honestly I don't care.
|
||||
When I use that ID to search log , if it pops more than things I care for, it is still no harm to me.</p>
|
||||
<p>My choice for this use case is <strong>rs/xid</strong>.
|
||||
Because it is small (not span too much on log line) and copy friendly.</p>
|
||||
<h2><a id="user-content-second-use-case-is-primary-key-also-hard-choice" class="anchor" aria-hidden="true" href="#second-use-case-is-primary-key-also-hard-choice"><span aria-hidden="true" class="octicon octicon-link"></span></a>Second use case is primary key, also hard choice</h2>
|
||||
<p>Why I don't use auto increment key for primary key?
|
||||
The answer is simple, I don't want to write database specific SQL.
|
||||
SQLite has some different syntax from MySQL, and PostgreSQL and so on.
|
||||
Every logic I can move to application layer from database layer, I will.</p>
|
||||
<p>In the past and present, I use <strong>google/uuid</strong>, specificially I use UUID v4.
|
||||
In the future I will look to use <strong>segmentio/ksuid</strong> and <strong>oklog/ulid</strong> (trial and error of course).
|
||||
Both are sortable, but <strong>google/uuid</strong> is not.
|
||||
The reason I'm afraid because the database is sensitive subject, and I need more testing and battle test proof to trust those libs.</p>
|
||||
<h2><a id="user-content-what-else" class="anchor" aria-hidden="true" href="#what-else"><span aria-hidden="true" class="octicon octicon-link"></span></a>What else?</h2>
|
||||
<p>I think about adding prefix to ID to identify which resource that ID represents.</p>
|
||||
<h2><a id="user-content-thanks" class="anchor" aria-hidden="true" href="#thanks"><span aria-hidden="true" class="octicon octicon-link"></span></a>Thanks</h2>
|
||||
<ul>
|
||||
<li><a href="https://www.cybertec-postgresql.com/en/uuid-serial-or-identity-columns-for-postgresql-auto-generated-primary-keys/" rel="nofollow">UUID, SERIAL OR IDENTITY COLUMNS FOR POSTGRESQL AUTO-GENERATED PRIMARY KEYS?</a></li>
|
||||
<li><a href="https://brandur.org/nanoglyphs/026-ids" rel="nofollow">Identity Crisis: Sequence v. UUID as Primary Key</a></li>
|
||||
<li><a href="https://blog.kowalczyk.info/article/JyRZ/generating-good-unique-ids-in-go.html" rel="nofollow">Generating good unique ids in Go</a></li>
|
||||
<li><a href="https://encore.dev/blog/go-1.18-generic-identifiers" rel="nofollow">How we used Go 1.18 when designing our Identifiers</a></li>
|
||||
<li><a href="https://blog.daveallie.com/ulid-primary-keys" rel="nofollow">ULIDs and Primary Keys</a></li>
|
||||
<li><a href="https://0pointer.net/blog/projects/ids.html" rel="nofollow">On IDs</a></li>
|
||||
</ul>
|
||||
<h1>
|
||||
<a
|
||||
id="user-content-uuid-or-else"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#uuid-or-else"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>UUID or else
|
||||
</h1>
|
||||
<p>
|
||||
There are many use cases where we need to use a unique ID. In my
|
||||
experience, I only encouter 2 cases:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
ID to trace request from client to server, from service to service
|
||||
(microservice architecture or nanoservice I don't know).
|
||||
</li>
|
||||
<li>Primary key for database.</li>
|
||||
</ul>
|
||||
<p>In my Go universe, there are some libs to help us with this:</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/google/uuid">google/uuid</a></li>
|
||||
<li><a href="https://github.com/rs/xid">rs/xid</a></li>
|
||||
<li><a href="https://github.com/segmentio/ksuid">segmentio/ksuid</a></li>
|
||||
<li><a href="https://github.com/oklog/ulid">oklog/ulid</a></li>
|
||||
</ul>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-first-use-case-is-trace-id-or-context-aware-id"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#first-use-case-is-trace-id-or-context-aware-id"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>First use case is trace ID, or context aware ID
|
||||
</h2>
|
||||
<p>
|
||||
The ID is used only for trace and log. If same ID is generated twice
|
||||
(because maybe the possibilty is too small but not 0), honestly I don't
|
||||
care. When I use that ID to search log , if it pops more than things I
|
||||
care for, it is still no harm to me.
|
||||
</p>
|
||||
<p>
|
||||
My choice for this use case is <strong>rs/xid</strong>. Because it is
|
||||
small (not span too much on log line) and copy friendly.
|
||||
</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-second-use-case-is-primary-key-also-hard-choice"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#second-use-case-is-primary-key-also-hard-choice"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Second use case is primary key, also hard choice
|
||||
</h2>
|
||||
<p>
|
||||
Why I don't use auto increment key for primary key? The answer is simple,
|
||||
I don't want to write database specific SQL. SQLite has some different
|
||||
syntax from MySQL, and PostgreSQL and so on. Every logic I can move to
|
||||
application layer from database layer, I will.
|
||||
</p>
|
||||
<p>
|
||||
In the past and present, I use <strong>google/uuid</strong>, specificially
|
||||
I use UUID v4. In the future I will look to use
|
||||
<strong>segmentio/ksuid</strong> and <strong>oklog/ulid</strong> (trial
|
||||
and error of course). Both are sortable, but
|
||||
<strong>google/uuid</strong> is not. The reason I'm afraid because the
|
||||
database is sensitive subject, and I need more testing and battle test
|
||||
proof to trust those libs.
|
||||
</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-what-else"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#what-else"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>What else?
|
||||
</h2>
|
||||
<p>
|
||||
I think about adding prefix to ID to identify which resource that ID
|
||||
represents.
|
||||
</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-thanks"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#thanks"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Thanks
|
||||
</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="https://www.cybertec-postgresql.com/en/uuid-serial-or-identity-columns-for-postgresql-auto-generated-primary-keys/"
|
||||
rel="nofollow"
|
||||
>UUID, SERIAL OR IDENTITY COLUMNS FOR POSTGRESQL AUTO-GENERATED
|
||||
PRIMARY KEYS?</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://brandur.org/nanoglyphs/026-ids" rel="nofollow"
|
||||
>Identity Crisis: Sequence v. UUID as Primary Key</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://blog.kowalczyk.info/article/JyRZ/generating-good-unique-ids-in-go.html"
|
||||
rel="nofollow"
|
||||
>Generating good unique ids in Go</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://encore.dev/blog/go-1.18-generic-identifiers"
|
||||
rel="nofollow"
|
||||
>How we used Go 1.18 when designing our Identifiers</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://blog.daveallie.com/ulid-primary-keys" rel="nofollow"
|
||||
>ULIDs and Primary Keys</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://0pointer.net/blog/projects/ids.html" rel="nofollow"
|
||||
>On IDs</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
Feel free to ask me via
|
||||
|
|
|
@ -43,26 +43,55 @@
|
|||
<div>
|
||||
<a href="index">Index</a>
|
||||
</div>
|
||||
<h1><a id="user-content-migrate-to-buf-from-prototool" class="anchor" aria-hidden="true" href="#migrate-to-buf-from-prototool"><span aria-hidden="true" class="octicon octicon-link"></span></a>Migrate to <code>buf</code> from <code>prototool</code>
|
||||
</h1>
|
||||
<p>Why? Because <code>prototool</code> is outdated, and can not run on M1 mac.</p>
|
||||
<p>We need 3 files:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>build.go</code>: need to install protoc-gen-* binaries with pin version in <code>go.mod</code>
|
||||
</li>
|
||||
<li><code>buf.yaml</code></li>
|
||||
<li><code>buf.gen.yaml</code></li>
|
||||
</ul>
|
||||
<p>FYI, the libs version I use:</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/golang/protobuf/releases/tag/v1.5.2">golang/protobuf v1.5.2</a></li>
|
||||
<li><a href="https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.16.0">grpc-ecosystem/grpc-gateway v1.16.0</a></li>
|
||||
<li><a href="github.com/envoyproxy/protoc-gen-validate">envoyproxy/protoc-gen-validate</a></li>
|
||||
<li><a href="github.com/kei2100/protoc-gen-marshal-zap">kei2100/protoc-gen-marshal-zap</a></li>
|
||||
</ul>
|
||||
<p><code>build.go</code>:</p>
|
||||
<div class="highlight highlight-source-go"><pre><span class="pl-c">//go:build tools</span>
|
||||
<h1>
|
||||
<a
|
||||
id="user-content-migrate-to-buf-from-prototool"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#migrate-to-buf-from-prototool"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Migrate to <code>buf</code> from <code>prototool</code>
|
||||
</h1>
|
||||
<p>
|
||||
Why? Because <code>prototool</code> is outdated, and can not run on M1
|
||||
mac.
|
||||
</p>
|
||||
<p>We need 3 files:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<code>build.go</code>: need to install protoc-gen-* binaries with pin
|
||||
version in <code>go.mod</code>
|
||||
</li>
|
||||
<li><code>buf.yaml</code></li>
|
||||
<li><code>buf.gen.yaml</code></li>
|
||||
</ul>
|
||||
<p>FYI, the libs version I use:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/golang/protobuf/releases/tag/v1.5.2"
|
||||
>golang/protobuf v1.5.2</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/grpc-ecosystem/grpc-gateway/releases/tag/v1.16.0"
|
||||
>grpc-ecosystem/grpc-gateway v1.16.0</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="github.com/envoyproxy/protoc-gen-validate"
|
||||
>envoyproxy/protoc-gen-validate</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="github.com/kei2100/protoc-gen-marshal-zap"
|
||||
>kei2100/protoc-gen-marshal-zap</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<p><code>build.go</code>:</p>
|
||||
<div class="highlight highlight-source-go">
|
||||
<pre><span class="pl-c">//go:build tools</span>
|
||||
<span class="pl-c">// +build tools</span>
|
||||
|
||||
<span class="pl-k">import</span> (
|
||||
|
@ -71,9 +100,11 @@
|
|||
_ <span class="pl-s">"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway"</span>
|
||||
_ <span class="pl-s">"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger"</span>
|
||||
_ <span class="pl-s">"github.com/kei2100/protoc-gen-marshal-zap/plugin/protoc-gen-marshal-zap"</span>
|
||||
)</pre></div>
|
||||
<p><code>buf.yaml</code></p>
|
||||
<div class="highlight highlight-source-yaml"><pre><span class="pl-ent">version</span>: <span class="pl-c1">v1</span>
|
||||
)</pre>
|
||||
</div>
|
||||
<p><code>buf.yaml</code></p>
|
||||
<div class="highlight highlight-source-yaml">
|
||||
<pre><span class="pl-ent">version</span>: <span class="pl-c1">v1</span>
|
||||
<span class="pl-ent">deps</span>:
|
||||
- <span class="pl-s">buf.build/haunt98/googleapis:b38d93f7ade94a698adff9576474ae7c</span>
|
||||
- <span class="pl-s">buf.build/haunt98/grpc-gateway:ecf4f0f58aa8496f8a76ed303c6e06c7</span>
|
||||
|
@ -84,9 +115,11 @@
|
|||
- <span class="pl-s">FILE</span>
|
||||
<span class="pl-ent">lint</span>:
|
||||
<span class="pl-ent">use</span>:
|
||||
- <span class="pl-s">DEFAULT</span></pre></div>
|
||||
<p><code>buf.gen.yaml</code>:</p>
|
||||
<div class="highlight highlight-source-yaml"><pre><span class="pl-ent">version</span>: <span class="pl-c1">v1</span>
|
||||
- <span class="pl-s">DEFAULT</span></pre>
|
||||
</div>
|
||||
<p><code>buf.gen.yaml</code>:</p>
|
||||
<div class="highlight highlight-source-yaml">
|
||||
<pre><span class="pl-ent">version</span>: <span class="pl-c1">v1</span>
|
||||
<span class="pl-ent">plugins</span>:
|
||||
- <span class="pl-ent">name</span>: <span class="pl-s">go</span>
|
||||
<span class="pl-ent">out</span>: <span class="pl-s">pkg</span>
|
||||
|
@ -105,9 +138,11 @@
|
|||
<span class="pl-ent">opt</span>:
|
||||
- <span class="pl-s">lang=go</span>
|
||||
- <span class="pl-ent">name</span>: <span class="pl-s">marshal-zap</span>
|
||||
<span class="pl-ent">out</span>: <span class="pl-s">pkg</span></pre></div>
|
||||
<p>Update <code>Makefile</code>:</p>
|
||||
<div class="highlight highlight-source-makefile"><pre><span class="pl-en">gen</span>:
|
||||
<span class="pl-ent">out</span>: <span class="pl-s">pkg</span></pre>
|
||||
</div>
|
||||
<p>Update <code>Makefile</code>:</p>
|
||||
<div class="highlight highlight-source-makefile">
|
||||
<pre><span class="pl-en">gen</span>:
|
||||
go install github.com/golang/protobuf/protoc-gen-go
|
||||
go install github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
|
||||
go install github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
|
||||
|
@ -116,32 +151,74 @@
|
|||
go install github.com/bufbuild/buf/cmd/buf@latest
|
||||
buf mod update
|
||||
buf format -w
|
||||
buf generate</pre></div>
|
||||
<p>Run <code>make gen</code> to have fun of course.</p>
|
||||
<h2><a id="user-content-faq" class="anchor" aria-hidden="true" href="#faq"><span aria-hidden="true" class="octicon octicon-link"></span></a>FAQ</h2>
|
||||
<p>Remember <code>grpc-ecosystem/grpc-gateway</code>, <code>envoyproxy/protoc-gen-validate</code>, <code>kei2100/protoc-gen-marshal-zap</code> is optional, so feel free to delete if you don't use theme.</p>
|
||||
<p>If use <code>vendor</code>:</p>
|
||||
<ul>
|
||||
<li>Replace <code>buf generate</code> with <code>buf generate --exclude-path vendor</code>.</li>
|
||||
<li>Replace <code>buf format -w</code> with <code>buf format -w --exclude-path vendor</code>.</li>
|
||||
</ul>
|
||||
<p>If you use grpc-gateway:</p>
|
||||
<ul>
|
||||
<li>Replace <code>import "third_party/googleapis/google/api/annotations.proto";</code> with <code>import "google/api/annotations.proto";</code>
|
||||
</li>
|
||||
<li>Delete <code>security_definitions</code>, <code>security</code>, in <code>option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger)</code>.</li>
|
||||
</ul>
|
||||
<p>The last step is delete <code>prototool.yaml</code>.</p>
|
||||
<p>If you are not migrate but start from scratch:</p>
|
||||
<ul>
|
||||
<li>Add <code>buf lint</code> to make sure your proto is good.</li>
|
||||
<li>Add <code>buf breaking --against "https://your-grpc-repo-goes-here.git"</code> to make sure each time you update proto, you don't break backward compatibility.</li>
|
||||
</ul>
|
||||
<h2><a id="user-content-thanks" class="anchor" aria-hidden="true" href="#thanks"><span aria-hidden="true" class="octicon octicon-link"></span></a>Thanks</h2>
|
||||
<ul>
|
||||
<li><a href="https://github.com/uber/prototool">uber/prototool</a></li>
|
||||
<li><a href="https://github.com/bufbuild/buf">bufbuild/buf</a></li>
|
||||
</ul>
|
||||
buf generate</pre>
|
||||
</div>
|
||||
<p>Run <code>make gen</code> to have fun of course.</p>
|
||||
<h2>
|
||||
<a id="user-content-faq" class="anchor" aria-hidden="true" href="#faq"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>FAQ
|
||||
</h2>
|
||||
<p>
|
||||
Remember <code>grpc-ecosystem/grpc-gateway</code>,
|
||||
<code>envoyproxy/protoc-gen-validate</code>,
|
||||
<code>kei2100/protoc-gen-marshal-zap</code> is optional, so feel free to
|
||||
delete if you don't use theme.
|
||||
</p>
|
||||
<p>If use <code>vendor</code>:</p>
|
||||
<ul>
|
||||
<li>
|
||||
Replace <code>buf generate</code> with
|
||||
<code>buf generate --exclude-path vendor</code>.
|
||||
</li>
|
||||
<li>
|
||||
Replace <code>buf format -w</code> with
|
||||
<code>buf format -w --exclude-path vendor</code>.
|
||||
</li>
|
||||
</ul>
|
||||
<p>If you use grpc-gateway:</p>
|
||||
<ul>
|
||||
<li>
|
||||
Replace
|
||||
<code
|
||||
>import "third_party/googleapis/google/api/annotations.proto";</code
|
||||
>
|
||||
with <code>import "google/api/annotations.proto";</code>
|
||||
</li>
|
||||
<li>
|
||||
Delete <code>security_definitions</code>, <code>security</code>, in
|
||||
<code
|
||||
>option
|
||||
(grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger)</code
|
||||
>.
|
||||
</li>
|
||||
</ul>
|
||||
<p>The last step is delete <code>prototool.yaml</code>.</p>
|
||||
<p>If you are not migrate but start from scratch:</p>
|
||||
<ul>
|
||||
<li>Add <code>buf lint</code> to make sure your proto is good.</li>
|
||||
<li>
|
||||
Add
|
||||
<code
|
||||
>buf breaking --against "https://your-grpc-repo-goes-here.git"</code
|
||||
>
|
||||
to make sure each time you update proto, you don't break backward
|
||||
compatibility.
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-thanks"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#thanks"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Thanks
|
||||
</h2>
|
||||
<ul>
|
||||
<li><a href="https://github.com/uber/prototool">uber/prototool</a></li>
|
||||
<li><a href="https://github.com/bufbuild/buf">bufbuild/buf</a></li>
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
Feel free to ask me via
|
||||
|
|
|
@ -43,12 +43,31 @@
|
|||
<div>
|
||||
<a href="index">Index</a>
|
||||
</div>
|
||||
<h1><a id="user-content-experiment-go" class="anchor" aria-hidden="true" href="#experiment-go"><span aria-hidden="true" class="octicon octicon-link"></span></a>Experiment Go</h1>
|
||||
<p>There come a time when you need to experiment new things, new style, new approach.
|
||||
So this post serves as it is named.</p>
|
||||
<h1><a id="user-content-design-api-by-trimming-down-the-interfacestruct-or-whatever" class="anchor" aria-hidden="true" href="#design-api-by-trimming-down-the-interfacestruct-or-whatever"><span aria-hidden="true" class="octicon octicon-link"></span></a>Design API by trimming down the interface/struct or whatever</h1>
|
||||
<p>Instead of:</p>
|
||||
<div class="highlight highlight-source-go"><pre><span class="pl-k">type</span> <span class="pl-smi">Client</span> <span class="pl-k">interface</span> {
|
||||
<h1>
|
||||
<a
|
||||
id="user-content-experiment-go"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#experiment-go"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Experiment Go
|
||||
</h1>
|
||||
<p>
|
||||
There come a time when you need to experiment new things, new style, new
|
||||
approach. So this post serves as it is named.
|
||||
</p>
|
||||
<h1>
|
||||
<a
|
||||
id="user-content-design-api-by-trimming-down-the-interfacestruct-or-whatever"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#design-api-by-trimming-down-the-interfacestruct-or-whatever"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Design API by trimming down the interface/struct or whatever
|
||||
</h1>
|
||||
<p>Instead of:</p>
|
||||
<div class="highlight highlight-source-go">
|
||||
<pre><span class="pl-k">type</span> <span class="pl-smi">Client</span> <span class="pl-k">interface</span> {
|
||||
<span class="pl-c1">GetUser</span>()
|
||||
<span class="pl-c1">AddUser</span>()
|
||||
<span class="pl-c1">GetAccount</span>()
|
||||
|
@ -57,9 +76,11 @@ So this post serves as it is named.</p>
|
|||
|
||||
<span class="pl-c">// c is Client</span>
|
||||
<span class="pl-s1">c</span>.<span class="pl-en">GetUser</span>()
|
||||
<span class="pl-s1">c</span>.<span class="pl-en">RemoveAccount</span>()</pre></div>
|
||||
<p>Try:</p>
|
||||
<div class="highlight highlight-source-go"><pre><span class="pl-k">type</span> <span class="pl-smi">Client</span> <span class="pl-k">struct</span> {
|
||||
<span class="pl-s1">c</span>.<span class="pl-en">RemoveAccount</span>()</pre>
|
||||
</div>
|
||||
<p>Try:</p>
|
||||
<div class="highlight highlight-source-go">
|
||||
<pre><span class="pl-k">type</span> <span class="pl-smi">Client</span> <span class="pl-k">struct</span> {
|
||||
<span class="pl-c1">User</span> <span class="pl-smi">ClientUser</span>
|
||||
<span class="pl-c1">Account</span> <span class="pl-smi">ClientAccount</span>
|
||||
}
|
||||
|
@ -76,30 +97,75 @@ So this post serves as it is named.</p>
|
|||
|
||||
<span class="pl-c">// c is Client</span>
|
||||
<span class="pl-s1">c</span>.<span class="pl-c1">User</span>.<span class="pl-en">Get</span>()
|
||||
<span class="pl-s1">c</span>.<span class="pl-c1">Account</span>.<span class="pl-en">Remove</span>()</pre></div>
|
||||
<p>The difference is <code>c.GetUser()</code> -> <code>c.User.Get()</code>.</p>
|
||||
<p>For example we have client which connect to bank.
|
||||
There are many functions like <code>GetUser</code>, <code>GetTransaction</code>, <code>VerifyAccount</code>, ...
|
||||
So split big client to many children, each child handle single aspect, like user or transaction.</p>
|
||||
<p>My concert is we replace an interface with a struct which contains multiple interfaces aka children.
|
||||
I don't know if this is the right call.</p>
|
||||
<p>This pattern is used by <a href="https://github.com/google/go-github">google/go-github</a>.</p>
|
||||
<h2><a id="user-content-find-alternative-to-grpcgrpc-go" class="anchor" aria-hidden="true" href="#find-alternative-to-grpcgrpc-go"><span aria-hidden="true" class="octicon octicon-link"></span></a>Find alternative to <a href="https://github.com/grpc/grpc-go">grpc/grpc-go</a>
|
||||
</h2>
|
||||
<p>Why?
|
||||
<a href="https://github.com/grpc/grpc-go/issues?q=is%3Aissue+compatibility+is%3Aclosed">See for yourself</a>.
|
||||
Also read <a href="https://go.dev/blog/protobuf-apiv2" rel="nofollow">A new Go API for Protocol Buffers</a> to know why <code>v1.20.0</code> is <code>v2</code>.</p>
|
||||
<p>Currently there are some:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/bufbuild/connect-go">bufbuild/connect-go</a>. Comming from buf, trust worthy but need time to make it match feature parity with grpc-go.</li>
|
||||
<li><a href="https://github.com/twitchtv/twirp">twitchtv/twirp</a></li>
|
||||
<li><a href="https://github.com/storj/drpc">storj/drpc</a></li>
|
||||
</ul>
|
||||
<h1><a id="user-content-thanks" class="anchor" aria-hidden="true" href="#thanks"><span aria-hidden="true" class="octicon octicon-link"></span></a>Thanks</h1>
|
||||
<ul>
|
||||
<li><a href="https://blog.gopheracademy.com/advent-2019/api-clients-humans/" rel="nofollow">API Clients for Humans</a></li>
|
||||
</ul>
|
||||
<span class="pl-s1">c</span>.<span class="pl-c1">Account</span>.<span class="pl-en">Remove</span>()</pre>
|
||||
</div>
|
||||
<p>
|
||||
The difference is <code>c.GetUser()</code> ->
|
||||
<code>c.User.Get()</code>.
|
||||
</p>
|
||||
<p>
|
||||
For example we have client which connect to bank. There are many functions
|
||||
like <code>GetUser</code>, <code>GetTransaction</code>,
|
||||
<code>VerifyAccount</code>, ... So split big client to many children, each
|
||||
child handle single aspect, like user or transaction.
|
||||
</p>
|
||||
<p>
|
||||
My concert is we replace an interface with a struct which contains
|
||||
multiple interfaces aka children. I don't know if this is the right call.
|
||||
</p>
|
||||
<p>
|
||||
This pattern is used by
|
||||
<a href="https://github.com/google/go-github">google/go-github</a>.
|
||||
</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-find-alternative-to-grpcgrpc-go"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#find-alternative-to-grpcgrpc-go"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Find alternative to
|
||||
<a href="https://github.com/grpc/grpc-go">grpc/grpc-go</a>
|
||||
</h2>
|
||||
<p>
|
||||
Why?
|
||||
<a
|
||||
href="https://github.com/grpc/grpc-go/issues?q=is%3Aissue+compatibility+is%3Aclosed"
|
||||
>See for yourself</a
|
||||
>. Also read
|
||||
<a href="https://go.dev/blog/protobuf-apiv2" rel="nofollow"
|
||||
>A new Go API for Protocol Buffers</a
|
||||
>
|
||||
to know why <code>v1.20.0</code> is <code>v2</code>.
|
||||
</p>
|
||||
<p>Currently there are some:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/bufbuild/connect-go">bufbuild/connect-go</a
|
||||
>. Comming from buf, trust worthy but need time to make it match feature
|
||||
parity with grpc-go.
|
||||
</li>
|
||||
<li><a href="https://github.com/twitchtv/twirp">twitchtv/twirp</a></li>
|
||||
<li><a href="https://github.com/storj/drpc">storj/drpc</a></li>
|
||||
</ul>
|
||||
<h1>
|
||||
<a
|
||||
id="user-content-thanks"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#thanks"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Thanks
|
||||
</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="https://blog.gopheracademy.com/advent-2019/api-clients-humans/"
|
||||
rel="nofollow"
|
||||
>API Clients for Humans</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
Feel free to ask me via
|
||||
|
|
|
@ -43,36 +43,93 @@
|
|||
<div>
|
||||
<a href="index">Index</a>
|
||||
</div>
|
||||
<h1><a id="user-content-sql" class="anchor" aria-hidden="true" href="#sql"><span aria-hidden="true" class="octicon octicon-link"></span></a>SQL</h1>
|
||||
<p>Previously in my fresher software developer time, I rarely write SQL, I always use ORM to wrap SQL.
|
||||
But time past and too much abstraction bites me.
|
||||
So I decide to only write SQL from now as much as possible, no more ORM for me.
|
||||
But if there is any cool ORM for Go, I guess I try.</p>
|
||||
<p>This guide is not kind of guide which cover all cases.
|
||||
Just my little tricks when I work with SQL.</p>
|
||||
<h2><a id="user-content-stay-away-from-database-unique-id" class="anchor" aria-hidden="true" href="#stay-away-from-database-unique-id"><span aria-hidden="true" class="octicon octicon-link"></span></a>Stay away from database unique id</h2>
|
||||
<p>Use UUID instead.
|
||||
If you can, and you should, choose UUID type which can be sortable.</p>
|
||||
<h2><a id="user-content-stay-away-from-database-timestamp" class="anchor" aria-hidden="true" href="#stay-away-from-database-timestamp"><span aria-hidden="true" class="octicon octicon-link"></span></a>Stay away from database timestamp</h2>
|
||||
<p>Stay away from all kind of database timestamp (MySQL timestmap, SQLite timestamp, ...)
|
||||
Just use int64 then pass the timestamp in service layer not database layer.</p>
|
||||
<p>Why? Because time and date and location are too much complex to handle.
|
||||
In my business, I use timestamp in milliseconds.
|
||||
Then I save timestamp as int64 value to database.
|
||||
Each time I get timestamp from database, I parse to time struct in Go with location or format I want.
|
||||
No more hassle!</p>
|
||||
<p>It looks like this:</p>
|
||||
<div class="highlight highlight-text-adblock"><pre>[Business] time, data -> convert to unix timestamp milliseconds -> [Database] int64</pre></div>
|
||||
<h2><a id="user-content-use-index" class="anchor" aria-hidden="true" href="#use-index"><span aria-hidden="true" class="octicon octicon-link"></span></a>Use index!!!</h2>
|
||||
<p>You should use index for faster query, but not too much.
|
||||
Don't create index for every fields in table.
|
||||
Choose wisely!</p>
|
||||
<p>For example, create index in MySQL:</p>
|
||||
<div class="highlight highlight-source-sql"><pre><span class="pl-k">CREATE</span> <span class="pl-k">INDEX</span> `<span class="pl-en">idx_timestamp</span>`
|
||||
<span class="pl-k">ON</span> <span class="pl-s"><span class="pl-pds">`</span>user_upload<span class="pl-pds">`</span></span> (<span class="pl-s"><span class="pl-pds">`</span>timestamp<span class="pl-pds">`</span></span>);</pre></div>
|
||||
<h2><a id="user-content-be-careful-with-null" class="anchor" aria-hidden="true" href="#be-careful-with-null"><span aria-hidden="true" class="octicon octicon-link"></span></a>Be careful with NULL</h2>
|
||||
<p>If compare with field which can be NULL, remember to check NULL for safety.</p>
|
||||
<div class="highlight highlight-source-sql"><pre><span class="pl-c"><span class="pl-c">--</span> field_something can be NULL</span>
|
||||
<h1>
|
||||
<a id="user-content-sql" class="anchor" aria-hidden="true" href="#sql"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>SQL
|
||||
</h1>
|
||||
<p>
|
||||
Previously in my fresher software developer time, I rarely write SQL, I
|
||||
always use ORM to wrap SQL. But time past and too much abstraction bites
|
||||
me. So I decide to only write SQL from now as much as possible, no more
|
||||
ORM for me. But if there is any cool ORM for Go, I guess I try.
|
||||
</p>
|
||||
<p>
|
||||
This guide is not kind of guide which cover all cases. Just my little
|
||||
tricks when I work with SQL.
|
||||
</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-stay-away-from-database-unique-id"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#stay-away-from-database-unique-id"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Stay away from database unique id
|
||||
</h2>
|
||||
<p>
|
||||
Use UUID instead. If you can, and you should, choose UUID type which can
|
||||
be sortable.
|
||||
</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-stay-away-from-database-timestamp"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#stay-away-from-database-timestamp"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Stay away from database timestamp
|
||||
</h2>
|
||||
<p>
|
||||
Stay away from all kind of database timestamp (MySQL timestmap, SQLite
|
||||
timestamp, ...) Just use int64 then pass the timestamp in service layer
|
||||
not database layer.
|
||||
</p>
|
||||
<p>
|
||||
Why? Because time and date and location are too much complex to handle. In
|
||||
my business, I use timestamp in milliseconds. Then I save timestamp as
|
||||
int64 value to database. Each time I get timestamp from database, I parse
|
||||
to time struct in Go with location or format I want. No more hassle!
|
||||
</p>
|
||||
<p>It looks like this:</p>
|
||||
<div class="highlight highlight-text-adblock">
|
||||
<pre>
|
||||
[Business] time, data -> convert to unix timestamp milliseconds -> [Database] int64</pre
|
||||
>
|
||||
</div>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-use-index"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#use-index"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Use index!!!
|
||||
</h2>
|
||||
<p>
|
||||
You should use index for faster query, but not too much. Don't create
|
||||
index for every fields in table. Choose wisely!
|
||||
</p>
|
||||
<p>For example, create index in MySQL:</p>
|
||||
<div class="highlight highlight-source-sql">
|
||||
<pre><span class="pl-k">CREATE</span> <span class="pl-k">INDEX</span> `<span class="pl-en">idx_timestamp</span>`
|
||||
<span class="pl-k">ON</span> <span class="pl-s"><span class="pl-pds">`</span>user_upload<span class="pl-pds">`</span></span> (<span class="pl-s"><span class="pl-pds">`</span>timestamp<span class="pl-pds">`</span></span>);</pre>
|
||||
</div>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-be-careful-with-null"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#be-careful-with-null"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Be careful with NULL
|
||||
</h2>
|
||||
<p>
|
||||
If compare with field which can be NULL, remember to check NULL for
|
||||
safety.
|
||||
</p>
|
||||
<div class="highlight highlight-source-sql">
|
||||
<pre><span class="pl-c"><span class="pl-c">--</span> field_something can be NULL</span>
|
||||
|
||||
<span class="pl-c"><span class="pl-c">--</span> Bad</span>
|
||||
<span class="pl-k">SELECT</span> <span class="pl-k">*</span>
|
||||
|
@ -82,34 +139,111 @@ Choose wisely!</p>
|
|||
<span class="pl-c"><span class="pl-c">--</span> Good</span>
|
||||
<span class="pl-k">SELECT</span> <span class="pl-k">*</span>
|
||||
<span class="pl-k">FROM</span> table
|
||||
<span class="pl-k">WHERE</span> (field_something IS <span class="pl-k">NULL</span> <span class="pl-k">OR</span> field_something <span class="pl-k">!=</span> <span class="pl-c1">1</span>)</pre></div>
|
||||
<p>Need clarify why this happpen? Idk :(</p>
|
||||
<h2><a id="user-content-varchar-or-text" class="anchor" aria-hidden="true" href="#varchar-or-text"><span aria-hidden="true" class="octicon octicon-link"></span></a>
|
||||
<code>VARCHAR</code> or <code>TEXT</code>
|
||||
</h2>
|
||||
<p>Prefer <code>VARCHAR</code> if you need to query and of course use index, and make sure size of value will never hit the limit.
|
||||
Prefer <code>TEXT</code> if you don't care, just want to store something.</p>
|
||||
<h2><a id="user-content-be-super-careful-when-migrate-update-database-on-production-and-online" class="anchor" aria-hidden="true" href="#be-super-careful-when-migrate-update-database-on-production-and-online"><span aria-hidden="true" class="octicon octicon-link"></span></a>Be super careful when migrate, update database on production and online!!!</h2>
|
||||
<p>Plase read docs about online ddl operations before do anything online (keep database running the same time update it, for example create index, ...)</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl-operations.html" rel="nofollow">For MySQL 5.7</a>, <a href="https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl-limitations.html" rel="nofollow">Limitations</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html" rel="nofollow">For MySQL 8.0</a>, <a href="https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-limitations.html" rel="nofollow">Limitations</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h2><a id="user-content-tools" class="anchor" aria-hidden="true" href="#tools"><span aria-hidden="true" class="octicon octicon-link"></span></a>Tools</h2>
|
||||
<ul>
|
||||
<li>Use <a href="https://github.com/sqlfluff/sqlfluff">sqlfluff/sqlfluff</a> to check your SQL.</li>
|
||||
<li>Use <a href="https://github.com/k1LoW/tbls">k1LoW/tbls</a> to grasp your database reality :)</li>
|
||||
</ul>
|
||||
<h2><a id="user-content-thanks" class="anchor" aria-hidden="true" href="#thanks"><span aria-hidden="true" class="octicon octicon-link"></span></a>Thanks</h2>
|
||||
<ul>
|
||||
<li><a href="https://use-the-index-luke.com/" rel="nofollow">Use The Index, Luke</a></li>
|
||||
<li><a href="https://www.foxhound.systems/blog/essential-elements-of-high-performance-sql-indexes/" rel="nofollow">Essential elements of high performance applications: SQL indexes</a></li>
|
||||
<li><a href="https://architecturenotes.co/things-you-should-know-about-databases/" rel="nofollow">Things You Should Know About Databases</a></li>
|
||||
</ul>
|
||||
<span class="pl-k">WHERE</span> (field_something IS <span class="pl-k">NULL</span> <span class="pl-k">OR</span> field_something <span class="pl-k">!=</span> <span class="pl-c1">1</span>)</pre>
|
||||
</div>
|
||||
<p>Need clarify why this happpen? Idk :(</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-varchar-or-text"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#varchar-or-text"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span
|
||||
></a>
|
||||
<code>VARCHAR</code> or <code>TEXT</code>
|
||||
</h2>
|
||||
<p>
|
||||
Prefer <code>VARCHAR</code> if you need to query and of course use index,
|
||||
and make sure size of value will never hit the limit. Prefer
|
||||
<code>TEXT</code> if you don't care, just want to store something.
|
||||
</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-be-super-careful-when-migrate-update-database-on-production-and-online"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#be-super-careful-when-migrate-update-database-on-production-and-online"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Be super careful when migrate, update database on production and
|
||||
online!!!
|
||||
</h2>
|
||||
<p>
|
||||
Plase read docs about online ddl operations before do anything online
|
||||
(keep database running the same time update it, for example create index,
|
||||
...)
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl-operations.html"
|
||||
rel="nofollow"
|
||||
>For MySQL 5.7</a
|
||||
>,
|
||||
<a
|
||||
href="https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl-limitations.html"
|
||||
rel="nofollow"
|
||||
>Limitations</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html"
|
||||
rel="nofollow"
|
||||
>For MySQL 8.0</a
|
||||
>,
|
||||
<a
|
||||
href="https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-limitations.html"
|
||||
rel="nofollow"
|
||||
>Limitations</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
<a id="user-content-tools" class="anchor" aria-hidden="true" href="#tools"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Tools
|
||||
</h2>
|
||||
<ul>
|
||||
<li>
|
||||
Use
|
||||
<a href="https://github.com/sqlfluff/sqlfluff">sqlfluff/sqlfluff</a> to
|
||||
check your SQL.
|
||||
</li>
|
||||
<li>
|
||||
Use <a href="https://github.com/k1LoW/tbls">k1LoW/tbls</a> to grasp your
|
||||
database reality :)
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-thanks"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#thanks"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Thanks
|
||||
</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://use-the-index-luke.com/" rel="nofollow"
|
||||
>Use The Index, Luke</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://www.foxhound.systems/blog/essential-elements-of-high-performance-sql-indexes/"
|
||||
rel="nofollow"
|
||||
>Essential elements of high performance applications: SQL indexes</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://architecturenotes.co/things-you-should-know-about-databases/"
|
||||
rel="nofollow"
|
||||
>Things You Should Know About Databases</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
Feel free to ask me via
|
||||
|
|
|
@ -43,10 +43,23 @@
|
|||
<div>
|
||||
<a href="index">Index</a>
|
||||
</div>
|
||||
<h1><a id="user-content-gitignore" class="anchor" aria-hidden="true" href="#gitignore"><span aria-hidden="true" class="octicon octicon-link"></span></a>gitignore</h1>
|
||||
<p>My quick check for <code>.gitignore</code>.</p>
|
||||
<h2><a id="user-content-base" class="anchor" aria-hidden="true" href="#base"><span aria-hidden="true" class="octicon octicon-link"></span></a>Base</h2>
|
||||
<div class="highlight highlight-text-adblock"><pre><span class="pl-c"># macOS</span>
|
||||
<h1>
|
||||
<a
|
||||
id="user-content-gitignore"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#gitignore"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>gitignore
|
||||
</h1>
|
||||
<p>My quick check for <code>.gitignore</code>.</p>
|
||||
<h2>
|
||||
<a id="user-content-base" class="anchor" aria-hidden="true" href="#base"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Base
|
||||
</h2>
|
||||
<div class="highlight highlight-text-adblock">
|
||||
<pre><span class="pl-c"># macOS</span>
|
||||
.DS_Store
|
||||
|
||||
<span class="pl-c"># Windows</span>
|
||||
|
@ -56,16 +69,31 @@
|
|||
.idea/
|
||||
|
||||
<span class="pl-c"># VSCode</span>
|
||||
.vscode/</pre></div>
|
||||
<h2><a id="user-content-go" class="anchor" aria-hidden="true" href="#go"><span aria-hidden="true" class="octicon octicon-link"></span></a>Go</h2>
|
||||
<div class="highlight highlight-text-adblock"><pre><span class="pl-c"># Go</span>
|
||||
.vscode/</pre>
|
||||
</div>
|
||||
<h2>
|
||||
<a id="user-content-go" class="anchor" aria-hidden="true" href="#go"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Go
|
||||
</h2>
|
||||
<div class="highlight highlight-text-adblock">
|
||||
<pre><span class="pl-c"># Go</span>
|
||||
<span class="pl-c"># Test coverage</span>
|
||||
coverage.out
|
||||
|
||||
<span class="pl-c"># Should ignore vendor</span>
|
||||
vendor</pre></div>
|
||||
<h2><a id="user-content-python" class="anchor" aria-hidden="true" href="#python"><span aria-hidden="true" class="octicon octicon-link"></span></a>Python</h2>
|
||||
<div class="highlight highlight-text-adblock"><pre>venv</pre></div>
|
||||
vendor</pre>
|
||||
</div>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-python"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#python"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Python
|
||||
</h2>
|
||||
<div class="highlight highlight-text-adblock"><pre>venv</pre></div>
|
||||
|
||||
<div>
|
||||
Feel free to ask me via
|
||||
|
|
|
@ -43,9 +43,18 @@
|
|||
<div>
|
||||
<a href="index">Index</a>
|
||||
</div>
|
||||
<h1><a id="user-content-reload-config" class="anchor" aria-hidden="true" href="#reload-config"><span aria-hidden="true" class="octicon octicon-link"></span></a>Reload config</h1>
|
||||
<p>This serves as design draft of reload config system</p>
|
||||
<div class="highlight highlight-source-wsd"><pre><span class="pl-k">@startuml</span> Reload config
|
||||
<h1>
|
||||
<a
|
||||
id="user-content-reload-config"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#reload-config"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Reload config
|
||||
</h1>
|
||||
<p>This serves as design draft of reload config system</p>
|
||||
<div class="highlight highlight-source-wsd">
|
||||
<pre><span class="pl-k">@startuml</span> Reload config
|
||||
|
||||
<span class="pl-k">skinparam</span> <span class="pl-k">defaultFontName</span> <span class="pl-s">Iosevka Term SS08</span>
|
||||
|
||||
|
@ -88,27 +97,46 @@
|
|||
|
||||
<span class="pl-k">deactivate</span> <span class="pl-c1">other_service</span>
|
||||
|
||||
<span class="pl-k">@enduml</span></pre></div>
|
||||
<p>Config storage can be any key value storage or database like etcd, Consul, mySQL, ...</p>
|
||||
<p>If storage is key value storage, maybe there is API to listen on config change.
|
||||
Otherwise we should create a loop to get all config from storage for some interval, for example each 5 minute.</p>
|
||||
<p>Each <code>other_service</code> need to get config from its memory, not hit <code>storage</code>.
|
||||
So there is some delay between upstream config (config in <code>storage</code>) and downstream config (config in <code>other_service</code>), but maybe we can forgive that delay (???).</p>
|
||||
<p>Pros:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Config can be dynamic, service does not need to restart to apply new config.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Each service only keep 1 connection to <code>storage</code> to listen to config change, not hit <code>storage</code> for each request.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Cons:</p>
|
||||
<ul>
|
||||
<li>Each service has 1 more dependency, aka <code>storage</code>.</li>
|
||||
<li>Need to handle fallback config, incase <code>storage</code> failure.</li>
|
||||
<li>Delay between upstream/downstream config</li>
|
||||
</ul>
|
||||
<span class="pl-k">@enduml</span></pre>
|
||||
</div>
|
||||
<p>
|
||||
Config storage can be any key value storage or database like etcd, Consul,
|
||||
mySQL, ...
|
||||
</p>
|
||||
<p>
|
||||
If storage is key value storage, maybe there is API to listen on config
|
||||
change. Otherwise we should create a loop to get all config from storage
|
||||
for some interval, for example each 5 minute.
|
||||
</p>
|
||||
<p>
|
||||
Each <code>other_service</code> need to get config from its memory, not
|
||||
hit <code>storage</code>. So there is some delay between upstream config
|
||||
(config in <code>storage</code>) and downstream config (config in
|
||||
<code>other_service</code>), but maybe we can forgive that delay (???).
|
||||
</p>
|
||||
<p>Pros:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
Config can be dynamic, service does not need to restart to apply new
|
||||
config.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
Each service only keep 1 connection to <code>storage</code> to listen
|
||||
to config change, not hit <code>storage</code> for each request.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Cons:</p>
|
||||
<ul>
|
||||
<li>Each service has 1 more dependency, aka <code>storage</code>.</li>
|
||||
<li>
|
||||
Need to handle fallback config, incase <code>storage</code> failure.
|
||||
</li>
|
||||
<li>Delay between upstream/downstream config</li>
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
Feel free to ask me via
|
||||
|
|
|
@ -43,90 +43,174 @@
|
|||
<div>
|
||||
<a href="index">Index</a>
|
||||
</div>
|
||||
<h1><a id="user-content-install-arch-linux" class="anchor" aria-hidden="true" href="#install-arch-linux"><span aria-hidden="true" class="octicon octicon-link"></span></a>Install Arch Linux</h1>
|
||||
<p>Install Arch Linux is thing I always want to do for my laptop/PC since I had my laptop in ninth grade.</p>
|
||||
<p>This is not a guide for everyone, this is just save for myself in a future and for anyone who want to walk in my shoes.</p>
|
||||
<h2><a id="user-content-installation-guide" class="anchor" aria-hidden="true" href="#installation-guide"><span aria-hidden="true" class="octicon octicon-link"></span></a><a href="https://wiki.archlinux.org/index.php/Installation_guide" rel="nofollow">Installation guide</a></h2>
|
||||
<h3><a id="user-content-pre-installation" class="anchor" aria-hidden="true" href="#pre-installation"><span aria-hidden="true" class="octicon octicon-link"></span></a>Pre-installation</h3>
|
||||
<p>Check disks carefully:</p>
|
||||
<div class="highlight highlight-source-shell"><pre>lsblk</pre></div>
|
||||
<p><a href="https://wiki.archlinux.org/index.php/USB_flash_installation_medium" rel="nofollow">USB flash installation medium</a></p>
|
||||
<h4><a id="user-content-verify-the-boot-mode" class="anchor" aria-hidden="true" href="#verify-the-boot-mode"><span aria-hidden="true" class="octicon octicon-link"></span></a>Verify the boot mode</h4>
|
||||
<p>Check UEFI mode:</p>
|
||||
<div class="highlight highlight-source-shell"><pre>ls /sys/firmware/efi/efivars</pre></div>
|
||||
<h4><a id="user-content-connect-to-the-internet" class="anchor" aria-hidden="true" href="#connect-to-the-internet"><span aria-hidden="true" class="octicon octicon-link"></span></a>Connect to the internet</h4>
|
||||
<p>For wifi, use <a href="https://wiki.archlinux.org/index.php/Iwd" rel="nofollow">iwd</a>.</p>
|
||||
<h4><a id="user-content-partition-the-disks" class="anchor" aria-hidden="true" href="#partition-the-disks"><span aria-hidden="true" class="octicon octicon-link"></span></a>Partition the disks</h4>
|
||||
<p><a href="https://wiki.archlinux.org/index.php/GPT_fdisk" rel="nofollow">GPT fdisk</a>:</p>
|
||||
<div class="highlight highlight-source-shell"><pre>cgdisk /dev/sdx</pre></div>
|
||||
<p><a href="https://wiki.archlinux.org/index.php/Partitioning#Partition_scheme" rel="nofollow">Partition scheme</a></p>
|
||||
<p>UEFI/GPT layout:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Mount point</th>
|
||||
<th>Partition</th>
|
||||
<th>Partition type</th>
|
||||
<th>Suggested size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>/mnt/efi</code></td>
|
||||
<td><code>/dev/efi_system_partition</code></td>
|
||||
<td>EFI System Partition</td>
|
||||
<td>512 MiB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/mnt/boot</code></td>
|
||||
<td><code>/dev/extended_boot_loader_partition</code></td>
|
||||
<td>Extended Boot Loader Partition</td>
|
||||
<td>1 GiB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/mnt</code></td>
|
||||
<td><code>/dev/root_partition</code></td>
|
||||
<td>Root Partition</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>BIOS/GPT layout:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Mount point</th>
|
||||
<th>Partition</th>
|
||||
<th>Partition type</th>
|
||||
<th>Suggested size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>BIOS boot partition</td>
|
||||
<td>1 MiB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/mnt</code></td>
|
||||
<td><code>/dev/root_partition</code></td>
|
||||
<td>Root Partition</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>LVM:</p>
|
||||
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#</span> Create physical volumes</span>
|
||||
<h1>
|
||||
<a
|
||||
id="user-content-install-arch-linux"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#install-arch-linux"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Install Arch Linux
|
||||
</h1>
|
||||
<p>
|
||||
Install Arch Linux is thing I always want to do for my laptop/PC since I
|
||||
had my laptop in ninth grade.
|
||||
</p>
|
||||
<p>
|
||||
This is not a guide for everyone, this is just save for myself in a future
|
||||
and for anyone who want to walk in my shoes.
|
||||
</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-installation-guide"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#installation-guide"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
><a
|
||||
href="https://wiki.archlinux.org/index.php/Installation_guide"
|
||||
rel="nofollow"
|
||||
>Installation guide</a
|
||||
>
|
||||
</h2>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-pre-installation"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#pre-installation"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Pre-installation
|
||||
</h3>
|
||||
<p>Check disks carefully:</p>
|
||||
<div class="highlight highlight-source-shell"><pre>lsblk</pre></div>
|
||||
<p>
|
||||
<a
|
||||
href="https://wiki.archlinux.org/index.php/USB_flash_installation_medium"
|
||||
rel="nofollow"
|
||||
>USB flash installation medium</a
|
||||
>
|
||||
</p>
|
||||
<h4>
|
||||
<a
|
||||
id="user-content-verify-the-boot-mode"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#verify-the-boot-mode"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Verify the boot mode
|
||||
</h4>
|
||||
<p>Check UEFI mode:</p>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre>ls /sys/firmware/efi/efivars</pre>
|
||||
</div>
|
||||
<h4>
|
||||
<a
|
||||
id="user-content-connect-to-the-internet"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#connect-to-the-internet"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Connect to the internet
|
||||
</h4>
|
||||
<p>
|
||||
For wifi, use
|
||||
<a href="https://wiki.archlinux.org/index.php/Iwd" rel="nofollow">iwd</a>.
|
||||
</p>
|
||||
<h4>
|
||||
<a
|
||||
id="user-content-partition-the-disks"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#partition-the-disks"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Partition the disks
|
||||
</h4>
|
||||
<p>
|
||||
<a href="https://wiki.archlinux.org/index.php/GPT_fdisk" rel="nofollow"
|
||||
>GPT fdisk</a
|
||||
>:
|
||||
</p>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre>cgdisk /dev/sdx</pre>
|
||||
</div>
|
||||
<p>
|
||||
<a
|
||||
href="https://wiki.archlinux.org/index.php/Partitioning#Partition_scheme"
|
||||
rel="nofollow"
|
||||
>Partition scheme</a
|
||||
>
|
||||
</p>
|
||||
<p>UEFI/GPT layout:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Mount point</th>
|
||||
<th>Partition</th>
|
||||
<th>Partition type</th>
|
||||
<th>Suggested size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>/mnt/efi</code></td>
|
||||
<td><code>/dev/efi_system_partition</code></td>
|
||||
<td>EFI System Partition</td>
|
||||
<td>512 MiB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/mnt/boot</code></td>
|
||||
<td><code>/dev/extended_boot_loader_partition</code></td>
|
||||
<td>Extended Boot Loader Partition</td>
|
||||
<td>1 GiB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/mnt</code></td>
|
||||
<td><code>/dev/root_partition</code></td>
|
||||
<td>Root Partition</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>BIOS/GPT layout:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Mount point</th>
|
||||
<th>Partition</th>
|
||||
<th>Partition type</th>
|
||||
<th>Suggested size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>BIOS boot partition</td>
|
||||
<td>1 MiB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/mnt</code></td>
|
||||
<td><code>/dev/root_partition</code></td>
|
||||
<td>Root Partition</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>LVM:</p>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre><span class="pl-c"><span class="pl-c">#</span> Create physical volumes</span>
|
||||
pvcreate /dev/sdaX
|
||||
|
||||
<span class="pl-c"><span class="pl-c">#</span> Create volume groups</span>
|
||||
vgcreate RootGroup /dev/sdaX /dev/sdaY
|
||||
|
||||
<span class="pl-c"><span class="pl-c">#</span> Create logical volumes</span>
|
||||
lvcreate -l +100%FREE RootGroup -n rootvol</pre></div>
|
||||
<p>Format:</p>
|
||||
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#</span> efi</span>
|
||||
lvcreate -l +100%FREE RootGroup -n rootvol</pre>
|
||||
</div>
|
||||
<p>Format:</p>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre><span class="pl-c"><span class="pl-c">#</span> efi</span>
|
||||
mkfs.fat -F32 /dev/efi_system_partition
|
||||
|
||||
<span class="pl-c"><span class="pl-c">#</span> boot</span>
|
||||
|
@ -139,9 +223,11 @@ mkfs.ext4 -L ROOT /dev/root_partition
|
|||
mkfs.btrfs -L ROOT /dev/root_partition
|
||||
|
||||
<span class="pl-c"><span class="pl-c">#</span> root on lvm</span>
|
||||
mkfs.ext4 /dev/RootGroup/rootvol</pre></div>
|
||||
<p>Mount:</p>
|
||||
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#</span> root</span>
|
||||
mkfs.ext4 /dev/RootGroup/rootvol</pre>
|
||||
</div>
|
||||
<p>Mount:</p>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre><span class="pl-c"><span class="pl-c">#</span> root</span>
|
||||
mount /dev/root_partition /mnt
|
||||
|
||||
<span class="pl-c"><span class="pl-c">#</span> root with btrfs</span>
|
||||
|
@ -154,9 +240,19 @@ mount /dev/RootGroup/rootvol /mnt
|
|||
mount --mkdir /dev/efi_system_partition /mnt/efi
|
||||
|
||||
<span class="pl-c"><span class="pl-c">#</span> boot</span>
|
||||
mount --mkdir /dev/extended_boot_loader_partition /mnt/boot</pre></div>
|
||||
<h3><a id="user-content-installation" class="anchor" aria-hidden="true" href="#installation"><span aria-hidden="true" class="octicon octicon-link"></span></a>Installation</h3>
|
||||
<div class="highlight highlight-source-shell"><pre>pacstrap -K /mnt base linux linux-firmware
|
||||
mount --mkdir /dev/extended_boot_loader_partition /mnt/boot</pre>
|
||||
</div>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-installation"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#installation"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Installation
|
||||
</h3>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre>pacstrap -K /mnt base linux linux-firmware
|
||||
|
||||
<span class="pl-c"><span class="pl-c">#</span> AMD</span>
|
||||
pacstrap -K /mnt amd-ucode
|
||||
|
@ -171,39 +267,125 @@ pacstrap -K /mnt btrfs-progs
|
|||
pacstrap -K /mnt lvm2
|
||||
|
||||
<span class="pl-c"><span class="pl-c">#</span> Text editor</span>
|
||||
pacstrap -K /mnt neovim</pre></div>
|
||||
<h3><a id="user-content-configure" class="anchor" aria-hidden="true" href="#configure"><span aria-hidden="true" class="octicon octicon-link"></span></a>Configure</h3>
|
||||
<h4><a id="user-content-fstab" class="anchor" aria-hidden="true" href="#fstab"><span aria-hidden="true" class="octicon octicon-link"></span></a><a href="https://wiki.archlinux.org/index.php/Fstab" rel="nofollow">fstab</a></h4>
|
||||
<div class="highlight highlight-source-shell"><pre>genfstab -U /mnt <span class="pl-k">>></span> /mnt/etc/fstab</pre></div>
|
||||
<h4><a id="user-content-chroot" class="anchor" aria-hidden="true" href="#chroot"><span aria-hidden="true" class="octicon octicon-link"></span></a>Chroot</h4>
|
||||
<div class="highlight highlight-source-shell"><pre>arch-chroot /mnt</pre></div>
|
||||
<h4><a id="user-content-time-zone" class="anchor" aria-hidden="true" href="#time-zone"><span aria-hidden="true" class="octicon octicon-link"></span></a>Time zone</h4>
|
||||
<div class="highlight highlight-source-shell"><pre>ln -sf /usr/share/zoneinfo/Region/City /etc/localtime
|
||||
pacstrap -K /mnt neovim</pre>
|
||||
</div>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-configure"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#configure"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Configure
|
||||
</h3>
|
||||
<h4>
|
||||
<a id="user-content-fstab" class="anchor" aria-hidden="true" href="#fstab"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
><a href="https://wiki.archlinux.org/index.php/Fstab" rel="nofollow"
|
||||
>fstab</a
|
||||
>
|
||||
</h4>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre>genfstab -U /mnt <span class="pl-k">>></span> /mnt/etc/fstab</pre>
|
||||
</div>
|
||||
<h4>
|
||||
<a
|
||||
id="user-content-chroot"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#chroot"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Chroot
|
||||
</h4>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre>arch-chroot /mnt</pre>
|
||||
</div>
|
||||
<h4>
|
||||
<a
|
||||
id="user-content-time-zone"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#time-zone"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Time zone
|
||||
</h4>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre>
|
||||
ln -sf /usr/share/zoneinfo/Region/City /etc/localtime
|
||||
|
||||
hwclock --systohc</pre></div>
|
||||
<h4><a id="user-content-localization" class="anchor" aria-hidden="true" href="#localization"><span aria-hidden="true" class="octicon octicon-link"></span></a>Localization:</h4>
|
||||
<p>Edit <code>/etc/locale.gen</code>:</p>
|
||||
<div class="highlight highlight-text-adblock"><pre><span class="pl-c"># Uncomment en_US.UTF-8 UTF-8</span></pre></div>
|
||||
<p>Generate locales:</p>
|
||||
<div class="highlight highlight-source-shell"><pre>locale-gen</pre></div>
|
||||
<p>Edit <code>/etc/locale.conf</code>:</p>
|
||||
<div class="highlight highlight-text-adblock"><pre>LANG=en_US.UTF-8</pre></div>
|
||||
<h4><a id="user-content-network-configuration" class="anchor" aria-hidden="true" href="#network-configuration"><span aria-hidden="true" class="octicon octicon-link"></span></a>Network configuration</h4>
|
||||
<p>Edit <code>/etc/hostname</code>:</p>
|
||||
<div class="highlight highlight-text-adblock"><pre>myhostname</pre></div>
|
||||
<h4><a id="user-content-initramfs" class="anchor" aria-hidden="true" href="#initramfs"><span aria-hidden="true" class="octicon octicon-link"></span></a>Initramfs</h4>
|
||||
<p>Edit <code>/etc/mkinitcpio.conf</code>:</p>
|
||||
<div class="highlight highlight-text-adblock"><pre><span class="pl-c"># LVM</span>
|
||||
hwclock --systohc</pre
|
||||
>
|
||||
</div>
|
||||
<h4>
|
||||
<a
|
||||
id="user-content-localization"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#localization"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Localization:
|
||||
</h4>
|
||||
<p>Edit <code>/etc/locale.gen</code>:</p>
|
||||
<div class="highlight highlight-text-adblock">
|
||||
<pre><span class="pl-c"># Uncomment en_US.UTF-8 UTF-8</span></pre>
|
||||
</div>
|
||||
<p>Generate locales:</p>
|
||||
<div class="highlight highlight-source-shell"><pre>locale-gen</pre></div>
|
||||
<p>Edit <code>/etc/locale.conf</code>:</p>
|
||||
<div class="highlight highlight-text-adblock">
|
||||
<pre>LANG=en_US.UTF-8</pre>
|
||||
</div>
|
||||
<h4>
|
||||
<a
|
||||
id="user-content-network-configuration"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#network-configuration"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Network configuration
|
||||
</h4>
|
||||
<p>Edit <code>/etc/hostname</code>:</p>
|
||||
<div class="highlight highlight-text-adblock"><pre>myhostname</pre></div>
|
||||
<h4>
|
||||
<a
|
||||
id="user-content-initramfs"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#initramfs"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Initramfs
|
||||
</h4>
|
||||
<p>Edit <code>/etc/mkinitcpio.conf</code>:</p>
|
||||
<div class="highlight highlight-text-adblock">
|
||||
<pre><span class="pl-c"># LVM</span>
|
||||
<span class="pl-c"># https://wiki.archlinux.org/title/Install_Arch_Linux_on_LVM#Adding_mkinitcpio_hooks</span>
|
||||
HOOKS=(base udev ... block lvm2 filesystems)
|
||||
|
||||
<span class="pl-c"># https://wiki.archlinux.org/title/mkinitcpio#Common_hooks</span>
|
||||
<span class="pl-c"># Replace udev with systemd</span></pre></div>
|
||||
<div class="highlight highlight-source-shell"><pre>mkinitcpio -P</pre></div>
|
||||
<h4><a id="user-content-root-password" class="anchor" aria-hidden="true" href="#root-password"><span aria-hidden="true" class="octicon octicon-link"></span></a>Root password</h4>
|
||||
<div class="highlight highlight-source-shell"><pre>passwd</pre></div>
|
||||
<h4><a id="user-content-addition" class="anchor" aria-hidden="true" href="#addition"><span aria-hidden="true" class="octicon octicon-link"></span></a>Addition</h4>
|
||||
<div class="highlight highlight-source-shell"><pre><span class="pl-c"><span class="pl-c">#</span> NetworkManager</span>
|
||||
<span class="pl-c"># Replace udev with systemd</span></pre>
|
||||
</div>
|
||||
<div class="highlight highlight-source-shell"><pre>mkinitcpio -P</pre></div>
|
||||
<h4>
|
||||
<a
|
||||
id="user-content-root-password"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#root-password"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Root password
|
||||
</h4>
|
||||
<div class="highlight highlight-source-shell"><pre>passwd</pre></div>
|
||||
<h4>
|
||||
<a
|
||||
id="user-content-addition"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#addition"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Addition
|
||||
</h4>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre><span class="pl-c"><span class="pl-c">#</span> NetworkManager</span>
|
||||
pacman -Syu networkmanager
|
||||
systemctl <span class="pl-c1">enable</span> NetworkManager.service
|
||||
|
||||
|
@ -212,15 +394,55 @@ pacman -Syu bluez
|
|||
systemctl <span class="pl-c1">enable</span> bluetooth.service
|
||||
|
||||
<span class="pl-c"><span class="pl-c">#</span> Clock</span>
|
||||
timedatectl set-ntp <span class="pl-c1">true</span></pre></div>
|
||||
<h4><a id="user-content-boot-loader" class="anchor" aria-hidden="true" href="#boot-loader"><span aria-hidden="true" class="octicon octicon-link"></span></a>Boot loader</h4>
|
||||
<p><a href="Applications/System/systemd-boot.md">systemd-boot</a></p>
|
||||
<p><a href="https://wiki.archlinux.org/index.php/GRUB" rel="nofollow">GRUB</a></p>
|
||||
<h2><a id="user-content-general-recommendations" class="anchor" aria-hidden="true" href="#general-recommendations"><span aria-hidden="true" class="octicon octicon-link"></span></a><a href="https://wiki.archlinux.org/index.php/General_recommendations" rel="nofollow">General recommendations</a></h2>
|
||||
<p>Always remember to check <strong>dependencies</strong> when install packages.</p>
|
||||
<h3><a id="user-content-system-administration" class="anchor" aria-hidden="true" href="#system-administration"><span aria-hidden="true" class="octicon octicon-link"></span></a>System administration</h3>
|
||||
<p><a href="https://wiki.archlinux.org/index.php/sudo" rel="nofollow">Sudo</a>:</p>
|
||||
<div class="highlight highlight-source-shell"><pre>pacman -Syu sudo
|
||||
timedatectl set-ntp <span class="pl-c1">true</span></pre>
|
||||
</div>
|
||||
<h4>
|
||||
<a
|
||||
id="user-content-boot-loader"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#boot-loader"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Boot loader
|
||||
</h4>
|
||||
<p><a href="Applications/System/systemd-boot.md">systemd-boot</a></p>
|
||||
<p>
|
||||
<a href="https://wiki.archlinux.org/index.php/GRUB" rel="nofollow"
|
||||
>GRUB</a
|
||||
>
|
||||
</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-general-recommendations"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#general-recommendations"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
><a
|
||||
href="https://wiki.archlinux.org/index.php/General_recommendations"
|
||||
rel="nofollow"
|
||||
>General recommendations</a
|
||||
>
|
||||
</h2>
|
||||
<p>
|
||||
Always remember to check <strong>dependencies</strong> when install
|
||||
packages.
|
||||
</p>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-system-administration"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#system-administration"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>System administration
|
||||
</h3>
|
||||
<p>
|
||||
<a href="https://wiki.archlinux.org/index.php/sudo" rel="nofollow">Sudo</a
|
||||
>:
|
||||
</p>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre>pacman -Syu sudo
|
||||
|
||||
EDITOR=nvim visudo
|
||||
<span class="pl-c"><span class="pl-c">#</span> Uncomment group wheel</span>
|
||||
|
@ -232,22 +454,53 @@ useradd -m -G wheel -c <span class="pl-s"><span class="pl-pds">"</span>The Joker
|
|||
useradd -m -G wheel -s /usr/bin/zsh -c <span class="pl-s"><span class="pl-pds">"</span>The Joker<span class="pl-pds">"</span></span> joker
|
||||
|
||||
<span class="pl-c"><span class="pl-c">#</span> Set password</span>
|
||||
passwd joker</pre></div>
|
||||
<p><a href="https://wiki.archlinux.org/index.php/Systemd-homed" rel="nofollow">systemd-homed (WIP)</a>:</p>
|
||||
<div class="highlight highlight-source-shell"><pre>systemctl <span class="pl-c1">enable</span> systemd-homed.service
|
||||
passwd joker</pre>
|
||||
</div>
|
||||
<p>
|
||||
<a
|
||||
href="https://wiki.archlinux.org/index.php/Systemd-homed"
|
||||
rel="nofollow"
|
||||
>systemd-homed (WIP)</a
|
||||
>:
|
||||
</p>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre>systemctl <span class="pl-c1">enable</span> systemd-homed.service
|
||||
|
||||
homectl create joker --real-name=<span class="pl-s"><span class="pl-pds">"</span>The Joker<span class="pl-pds">"</span></span> --member-of=wheel
|
||||
|
||||
<span class="pl-c"><span class="pl-c">#</span> Using zsh</span>
|
||||
homectl update joker --shell=/usr/bin/zsh</pre></div>
|
||||
<p><strong>Note</strong>:
|
||||
Can not run <code>homectl</code> when install Arch Linux.
|
||||
Should run on the first boot.</p>
|
||||
<h3><a id="user-content-desktop-environment" class="anchor" aria-hidden="true" href="#desktop-environment"><span aria-hidden="true" class="octicon octicon-link"></span></a>Desktop Environment</h3>
|
||||
<p>Install <a href="https://wiki.archlinux.org/index.php/Xorg" rel="nofollow">Xorg</a>:</p>
|
||||
<div class="highlight highlight-source-shell"><pre>pacman -Syu xorg-server</pre></div>
|
||||
<h4><a id="user-content-gnome" class="anchor" aria-hidden="true" href="#gnome"><span aria-hidden="true" class="octicon octicon-link"></span></a><a href="https://wiki.archlinux.org/index.php/GNOME" rel="nofollow">GNOME</a></h4>
|
||||
<div class="highlight highlight-source-shell"><pre>pacman -Syu gnome-shell \
|
||||
homectl update joker --shell=/usr/bin/zsh</pre>
|
||||
</div>
|
||||
<p>
|
||||
<strong>Note</strong>: Can not run <code>homectl</code> when install Arch
|
||||
Linux. Should run on the first boot.
|
||||
</p>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-desktop-environment"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#desktop-environment"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Desktop Environment
|
||||
</h3>
|
||||
<p>
|
||||
Install
|
||||
<a href="https://wiki.archlinux.org/index.php/Xorg" rel="nofollow">Xorg</a
|
||||
>:
|
||||
</p>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre>pacman -Syu xorg-server</pre>
|
||||
</div>
|
||||
<h4>
|
||||
<a id="user-content-gnome" class="anchor" aria-hidden="true" href="#gnome"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
><a href="https://wiki.archlinux.org/index.php/GNOME" rel="nofollow"
|
||||
>GNOME</a
|
||||
>
|
||||
</h4>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre>pacman -Syu gnome-shell \
|
||||
gnome-control-center gnome-system-monitor \
|
||||
gnome-tweaks gnome-backgrounds gnome-screenshot gnome-keyring gnome-logs \
|
||||
gnome-console gnome-text-editor \
|
||||
|
@ -255,37 +508,164 @@ Should run on the first boot.</p>
|
|||
|
||||
<span class="pl-c"><span class="pl-c">#</span> Login manager</span>
|
||||
pacman -Syu gdm
|
||||
systemctl <span class="pl-c1">enable</span> gdm.service</pre></div>
|
||||
<h4><a id="user-content-kde-wip" class="anchor" aria-hidden="true" href="#kde-wip"><span aria-hidden="true" class="octicon octicon-link"></span></a><a href="https://wiki.archlinux.org/title/KDE" rel="nofollow">KDE (WIP)</a></h4>
|
||||
<div class="highlight highlight-source-shell"><pre>pacman -Syu plasma-meta \
|
||||
systemctl <span class="pl-c1">enable</span> gdm.service</pre>
|
||||
</div>
|
||||
<h4>
|
||||
<a
|
||||
id="user-content-kde-wip"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#kde-wip"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
><a href="https://wiki.archlinux.org/title/KDE" rel="nofollow"
|
||||
>KDE (WIP)</a
|
||||
>
|
||||
</h4>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre>pacman -Syu plasma-meta \
|
||||
kde-system-meta
|
||||
|
||||
<span class="pl-c"><span class="pl-c">#</span> Login manager</span>
|
||||
pacman -Syu sddm
|
||||
systemctl <span class="pl-c1">enable</span> sddm.service</pre></div>
|
||||
<h2><a id="user-content-list-of-applications" class="anchor" aria-hidden="true" href="#list-of-applications"><span aria-hidden="true" class="octicon octicon-link"></span></a><a href="https://wiki.archlinux.org/index.php/List_of_applications" rel="nofollow">List of applications</a></h2>
|
||||
<h3><a id="user-content-pacman" class="anchor" aria-hidden="true" href="#pacman"><span aria-hidden="true" class="octicon octicon-link"></span></a><a href="https://wiki.archlinux.org/index.php/pacman" rel="nofollow">pacman</a></h3>
|
||||
<p>Uncomment in <code>/etc/pacman.conf</code>:</p>
|
||||
<div class="highlight highlight-text-adblock"><pre><span class="pl-c"># Misc options</span>
|
||||
systemctl <span class="pl-c1">enable</span> sddm.service</pre>
|
||||
</div>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-list-of-applications"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#list-of-applications"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
><a
|
||||
href="https://wiki.archlinux.org/index.php/List_of_applications"
|
||||
rel="nofollow"
|
||||
>List of applications</a
|
||||
>
|
||||
</h2>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-pacman"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#pacman"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
><a href="https://wiki.archlinux.org/index.php/pacman" rel="nofollow"
|
||||
>pacman</a
|
||||
>
|
||||
</h3>
|
||||
<p>Uncomment in <code>/etc/pacman.conf</code>:</p>
|
||||
<div class="highlight highlight-text-adblock">
|
||||
<pre><span class="pl-c"># Misc options</span>
|
||||
Color
|
||||
ParallelDownloads</pre></div>
|
||||
<h3><a id="user-content-pipewire-wip" class="anchor" aria-hidden="true" href="#pipewire-wip"><span aria-hidden="true" class="octicon octicon-link"></span></a><a href="https://wiki.archlinux.org/title/PipeWire" rel="nofollow">Pipewire (WIP)</a></h3>
|
||||
<div class="highlight highlight-source-shell"><pre>pacman -Syu pipewire wireplumber \
|
||||
ParallelDownloads</pre>
|
||||
</div>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-pipewire-wip"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#pipewire-wip"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
><a href="https://wiki.archlinux.org/title/PipeWire" rel="nofollow"
|
||||
>Pipewire (WIP)</a
|
||||
>
|
||||
</h3>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre>
|
||||
pacman -Syu pipewire wireplumber \
|
||||
pipewire-alsa pipewire-pulse \
|
||||
gst-plugin-pipewire pipewire-v4l2</pre></div>
|
||||
<h3><a id="user-content-flatpak-wip" class="anchor" aria-hidden="true" href="#flatpak-wip"><span aria-hidden="true" class="octicon octicon-link"></span></a><a href="https://wiki.archlinux.org/title/Flatpak" rel="nofollow">Flatpak (WIP)</a></h3>
|
||||
<div class="highlight highlight-source-shell"><pre>pacman -Syu flatpak</pre></div>
|
||||
<h2><a id="user-content-improving-performance" class="anchor" aria-hidden="true" href="#improving-performance"><span aria-hidden="true" class="octicon octicon-link"></span></a><a href="https://wiki.archlinux.org/index.php/improving_performance" rel="nofollow">Improving performance</a></h2>
|
||||
<p><a href="https://wiki.archlinux.org/index.php/swap#Swap_file" rel="nofollow">https://wiki.archlinux.org/index.php/swap#Swap_file</a></p>
|
||||
<p><a href="https://wiki.archlinux.org/index.php/swap#Swappiness" rel="nofollow">https://wiki.archlinux.org/index.php/swap#Swappiness</a></p>
|
||||
<p><a href="https://wiki.archlinux.org/index.php/Systemd/Journal#Journal_size_limit" rel="nofollow">https://wiki.archlinux.org/index.php/Systemd/Journal#Journal_size_limit</a></p>
|
||||
<p><a href="https://wiki.archlinux.org/index.php/Core_dump#Disabling_automatic_core_dumps" rel="nofollow">https://wiki.archlinux.org/index.php/Core_dump#Disabling_automatic_core_dumps</a></p>
|
||||
<p><a href="https://wiki.archlinux.org/index.php/Solid_state_drive#Periodic_TRIM" rel="nofollow">https://wiki.archlinux.org/index.php/Solid_state_drive#Periodic_TRIM</a></p>
|
||||
<p><a href="https://wiki.archlinux.org/index.php/Silent_boot" rel="nofollow">https://wiki.archlinux.org/index.php/Silent_boot</a></p>
|
||||
<p><a href="https://wiki.archlinux.org/title/Improving_performance#Watchdogs" rel="nofollow">https://wiki.archlinux.org/title/Improving_performance#Watchdogs</a></p>
|
||||
<p><a href="https://wiki.archlinux.org/title/PRIME" rel="nofollow">https://wiki.archlinux.org/title/PRIME</a></p>
|
||||
<h2><a id="user-content-in-the-end" class="anchor" aria-hidden="true" href="#in-the-end"><span aria-hidden="true" class="octicon octicon-link"></span></a>In the end</h2>
|
||||
<p>This guide is updated regularly I promise.</p>
|
||||
gst-plugin-pipewire pipewire-v4l2</pre
|
||||
>
|
||||
</div>
|
||||
<h3>
|
||||
<a
|
||||
id="user-content-flatpak-wip"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#flatpak-wip"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
><a href="https://wiki.archlinux.org/title/Flatpak" rel="nofollow"
|
||||
>Flatpak (WIP)</a
|
||||
>
|
||||
</h3>
|
||||
<div class="highlight highlight-source-shell">
|
||||
<pre>pacman -Syu flatpak</pre>
|
||||
</div>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-improving-performance"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#improving-performance"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
><a
|
||||
href="https://wiki.archlinux.org/index.php/improving_performance"
|
||||
rel="nofollow"
|
||||
>Improving performance</a
|
||||
>
|
||||
</h2>
|
||||
<p>
|
||||
<a
|
||||
href="https://wiki.archlinux.org/index.php/swap#Swap_file"
|
||||
rel="nofollow"
|
||||
>https://wiki.archlinux.org/index.php/swap#Swap_file</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a
|
||||
href="https://wiki.archlinux.org/index.php/swap#Swappiness"
|
||||
rel="nofollow"
|
||||
>https://wiki.archlinux.org/index.php/swap#Swappiness</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a
|
||||
href="https://wiki.archlinux.org/index.php/Systemd/Journal#Journal_size_limit"
|
||||
rel="nofollow"
|
||||
>https://wiki.archlinux.org/index.php/Systemd/Journal#Journal_size_limit</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a
|
||||
href="https://wiki.archlinux.org/index.php/Core_dump#Disabling_automatic_core_dumps"
|
||||
rel="nofollow"
|
||||
>https://wiki.archlinux.org/index.php/Core_dump#Disabling_automatic_core_dumps</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a
|
||||
href="https://wiki.archlinux.org/index.php/Solid_state_drive#Periodic_TRIM"
|
||||
rel="nofollow"
|
||||
>https://wiki.archlinux.org/index.php/Solid_state_drive#Periodic_TRIM</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://wiki.archlinux.org/index.php/Silent_boot" rel="nofollow"
|
||||
>https://wiki.archlinux.org/index.php/Silent_boot</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a
|
||||
href="https://wiki.archlinux.org/title/Improving_performance#Watchdogs"
|
||||
rel="nofollow"
|
||||
>https://wiki.archlinux.org/title/Improving_performance#Watchdogs</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://wiki.archlinux.org/title/PRIME" rel="nofollow"
|
||||
>https://wiki.archlinux.org/title/PRIME</a
|
||||
>
|
||||
</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-in-the-end"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#in-the-end"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>In the end
|
||||
</h2>
|
||||
<p>This guide is updated regularly I promise.</p>
|
||||
|
||||
<div>
|
||||
Feel free to ask me via
|
||||
|
|
|
@ -43,10 +43,16 @@
|
|||
<div>
|
||||
<a href="index">Index</a>
|
||||
</div>
|
||||
<h1><a id="user-content-integration-go-grpc-with-buf" class="anchor" aria-hidden="true" href="#integration-go-grpc-with-buf"><span aria-hidden="true" class="octicon octicon-link"></span></a>Integration Go gRPC with Buf</h1>
|
||||
<p>There are 2 questions here.
|
||||
What is Buf?
|
||||
And why is Buf?</p>
|
||||
<h1>
|
||||
<a
|
||||
id="user-content-integration-go-grpc-with-buf"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#integration-go-grpc-with-buf"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Integration Go gRPC with Buf
|
||||
</h1>
|
||||
<p>There are 2 questions here. What is Buf? And why is Buf?</p>
|
||||
|
||||
<div>
|
||||
Feel free to ask me via
|
||||
|
|
|
@ -43,38 +43,88 @@
|
|||
<div>
|
||||
<a href="index">Index</a>
|
||||
</div>
|
||||
<h1><a id="user-content-speed-up-writing-go-test-asap" class="anchor" aria-hidden="true" href="#speed-up-writing-go-test-asap"><span aria-hidden="true" class="octicon octicon-link"></span></a>Speed up writing Go test ASAP</h1>
|
||||
<p>Imagine your project currently have 0% unit test code coverage.
|
||||
And your boss keep pushing it to 80% or even 90%?
|
||||
What do you do?
|
||||
Give up?</p>
|
||||
<p>What if I tell you there is a way?
|
||||
Not entirely cheating but ... you know, there is always trade off.</p>
|
||||
<p>If your purpose is to test carefully all path, check if all return is correctly.
|
||||
Sadly this post is not for you, I guess.
|
||||
If you only want good number on test coverage, with minimum effort as possible, I hope this will show you some idea you can use :)</p>
|
||||
<p>In my opinion, unit test is not that important (like must must have).
|
||||
It's just make sure your code is running excatly as you intent it to be.
|
||||
If you don't think about edge case before, unit test won't help you.</p>
|
||||
<h2><a id="user-content-first-rewrite-the-impossible-to-test-out" class="anchor" aria-hidden="true" href="#first-rewrite-the-impossible-to-test-out"><span aria-hidden="true" class="octicon octicon-link"></span></a>First, rewrite the impossible (to test) out</h2>
|
||||
<p>When I learn programming, I encounter very interesting idea, which become mainly my mindset when I dev later.
|
||||
I don't recall it clearly, kinda like: "Don't just fix bugs, rewrite it so that kind of bugs will not appear again".
|
||||
So in our context, there is some thing we hardly or can not write test in Go.
|
||||
My suggestion is don't use that thing.</p>
|
||||
<p>In my experience, I can list a few here:</p>
|
||||
<ul>
|
||||
<li>Read config each time call func (<code>viper.Get...</code>). You can and you should init all config when project starts.</li>
|
||||
<li>Not use Dependency Injection (DI). There are too many posts in Internet tell you how to do DI properly.</li>
|
||||
<li>Use global var (Except global var <code>Err...</code>). You should move all global var to fields inside some struct.</li>
|
||||
</ul>
|
||||
<h2><a id="user-content-let-the-fun-writing-test-begin" class="anchor" aria-hidden="true" href="#let-the-fun-writing-test-begin"><span aria-hidden="true" class="octicon octicon-link"></span></a>Let the fun (writing test) begin</h2>
|
||||
<p>If you code Go long enough, you know table driven tests and how is that so useful.
|
||||
You set up test data, then you test.
|
||||
Somewhere in the future, you change the func, then you need to update test data, then you good!</p>
|
||||
<p>In simple case, your func only have 2 or 3 inputs so table drive tests is still looking good.
|
||||
But real world is ugly (maybe not, idk I'm just too young in this industry). Your func can have 5 or 10 inputs, also your func call many third party services.</p>
|
||||
<p>Imagine having below func to upload image:</p>
|
||||
<div class="highlight highlight-source-go"><pre><span class="pl-k">type</span> <span class="pl-smi">service</span> <span class="pl-k">struct</span> {
|
||||
<h1>
|
||||
<a
|
||||
id="user-content-speed-up-writing-go-test-asap"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#speed-up-writing-go-test-asap"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Speed up writing Go test ASAP
|
||||
</h1>
|
||||
<p>
|
||||
Imagine your project currently have 0% unit test code coverage. And your
|
||||
boss keep pushing it to 80% or even 90%? What do you do? Give up?
|
||||
</p>
|
||||
<p>
|
||||
What if I tell you there is a way? Not entirely cheating but ... you know,
|
||||
there is always trade off.
|
||||
</p>
|
||||
<p>
|
||||
If your purpose is to test carefully all path, check if all return is
|
||||
correctly. Sadly this post is not for you, I guess. If you only want good
|
||||
number on test coverage, with minimum effort as possible, I hope this will
|
||||
show you some idea you can use :)
|
||||
</p>
|
||||
<p>
|
||||
In my opinion, unit test is not that important (like must must have). It's
|
||||
just make sure your code is running excatly as you intent it to be. If you
|
||||
don't think about edge case before, unit test won't help you.
|
||||
</p>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-first-rewrite-the-impossible-to-test-out"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#first-rewrite-the-impossible-to-test-out"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>First, rewrite the impossible (to test) out
|
||||
</h2>
|
||||
<p>
|
||||
When I learn programming, I encounter very interesting idea, which become
|
||||
mainly my mindset when I dev later. I don't recall it clearly, kinda like:
|
||||
"Don't just fix bugs, rewrite it so that kind of bugs will not appear
|
||||
again". So in our context, there is some thing we hardly or can not write
|
||||
test in Go. My suggestion is don't use that thing.
|
||||
</p>
|
||||
<p>In my experience, I can list a few here:</p>
|
||||
<ul>
|
||||
<li>
|
||||
Read config each time call func (<code>viper.Get...</code>). You can and
|
||||
you should init all config when project starts.
|
||||
</li>
|
||||
<li>
|
||||
Not use Dependency Injection (DI). There are too many posts in Internet
|
||||
tell you how to do DI properly.
|
||||
</li>
|
||||
<li>
|
||||
Use global var (Except global var <code>Err...</code>). You should move
|
||||
all global var to fields inside some struct.
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
<a
|
||||
id="user-content-let-the-fun-writing-test-begin"
|
||||
class="anchor"
|
||||
aria-hidden="true"
|
||||
href="#let-the-fun-writing-test-begin"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Let the fun (writing test) begin
|
||||
</h2>
|
||||
<p>
|
||||
If you code Go long enough, you know table driven tests and how is that so
|
||||
useful. You set up test data, then you test. Somewhere in the future, you
|
||||
change the func, then you need to update test data, then you good!
|
||||
</p>
|
||||
<p>
|
||||
In simple case, your func only have 2 or 3 inputs so table drive tests is
|
||||
still looking good. But real world is ugly (maybe not, idk I'm just too
|
||||
young in this industry). Your func can have 5 or 10 inputs, also your func
|
||||
call many third party services.
|
||||
</p>
|
||||
<p>Imagine having below func to upload image:</p>
|
||||
<div class="highlight highlight-source-go">
|
||||
<pre><span class="pl-k">type</span> <span class="pl-smi">service</span> <span class="pl-k">struct</span> {
|
||||
<span class="pl-c1">db</span> <span class="pl-smi">DB</span>
|
||||
<span class="pl-c1">redis</span> <span class="pl-smi">Redis</span>
|
||||
<span class="pl-c1">minio</span> <span class="pl-smi">MinIO</span>
|
||||
|
@ -105,9 +155,15 @@ But real world is ugly (maybe not, idk I'm just too young in this industry). You
|
|||
}
|
||||
|
||||
<span class="pl-k">return</span> <span class="pl-c1">nil</span>
|
||||
}</pre></div>
|
||||
<p>With table driven test and thanks to <a href="https://github.com/stretchr/testify">stretchr/testify</a>, I usually write like this:</p>
|
||||
<div class="highlight highlight-source-go"><pre><span class="pl-k">type</span> <span class="pl-smi">ServiceSuite</span> <span class="pl-k">struct</span> {
|
||||
}</pre>
|
||||
</div>
|
||||
<p>
|
||||
With table driven test and thanks to
|
||||
<a href="https://github.com/stretchr/testify">stretchr/testify</a>, I
|
||||
usually write like this:
|
||||
</p>
|
||||
<div class="highlight highlight-source-go">
|
||||
<pre><span class="pl-k">type</span> <span class="pl-smi">ServiceSuite</span> <span class="pl-k">struct</span> {
|
||||
suite.<span class="pl-smi">Suite</span>
|
||||
|
||||
<span class="pl-c1">db</span> <span class="pl-smi">DBMock</span>
|
||||
|
@ -152,16 +208,20 @@ But real world is ugly (maybe not, idk I'm just too young in this industry). You
|
|||
<span class="pl-s1">s</span>.<span class="pl-en">Equal</span>(<span class="pl-s1">wantErr</span>, <span class="pl-s1">gotErr</span>)
|
||||
})
|
||||
}
|
||||
}</pre></div>
|
||||
<p>Looks good right?
|
||||
Be careful with this.
|
||||
It can go from 0 to 100 ugly real quick.</p>
|
||||
<p>What if req is a struct with many fields?
|
||||
So in each test case you need to set up req.
|
||||
They are almost the same, but with some error case you must alter req.
|
||||
It's easy to be init with wrong value here (typing maybe ?).
|
||||
Also all req looks similiar, kinda duplicated.</p>
|
||||
<div class="highlight highlight-source-go"><pre><span class="pl-s1">tests</span> <span class="pl-c1">:=</span> []<span class="pl-k">struct</span>{
|
||||
}</pre>
|
||||
</div>
|
||||
<p>
|
||||
Looks good right? Be careful with this. It can go from 0 to 100 ugly real
|
||||
quick.
|
||||
</p>
|
||||
<p>
|
||||
What if req is a struct with many fields? So in each test case you need to
|
||||
set up req. They are almost the same, but with some error case you must
|
||||
alter req. It's easy to be init with wrong value here (typing maybe ?).
|
||||
Also all req looks similiar, kinda duplicated.
|
||||
</p>
|
||||
<div class="highlight highlight-source-go">
|
||||
<pre><span class="pl-s1">tests</span> <span class="pl-c1">:=</span> []<span class="pl-k">struct</span>{
|
||||
<span class="pl-c1">name</span> <span class="pl-smi">string</span>
|
||||
<span class="pl-c1">req</span> <span class="pl-smi">Request</span>
|
||||
<span class="pl-c1">verifyErr</span> <span class="pl-smi">error</span>
|
||||
|
@ -207,10 +267,14 @@ Also all req looks similiar, kinda duplicated.</p>
|
|||
}
|
||||
<span class="pl-c">// Other fieles</span>
|
||||
}
|
||||
}</pre></div>
|
||||
<p>What if dependencies of service keep growing?
|
||||
More mock error to test data of course.</p>
|
||||
<div class="highlight highlight-source-go"><pre> <span class="pl-s1">tests</span> <span class="pl-c1">:=</span> []<span class="pl-k">struct</span>{
|
||||
}</pre>
|
||||
</div>
|
||||
<p>
|
||||
What if dependencies of service keep growing? More mock error to test data
|
||||
of course.
|
||||
</p>
|
||||
<div class="highlight highlight-source-go">
|
||||
<pre> <span class="pl-s1">tests</span> <span class="pl-c1">:=</span> []<span class="pl-k">struct</span>{
|
||||
<span class="pl-c1">name</span> <span class="pl-smi">string</span>
|
||||
<span class="pl-c1">req</span> <span class="pl-smi">Request</span>
|
||||
<span class="pl-c1">verifyErr</span> <span class="pl-smi">error</span>
|
||||
|
@ -228,15 +292,30 @@ More mock error to test data of course.</p>
|
|||
{
|
||||
<span class="pl-c">// Init test case</span>
|
||||
}
|
||||
}</pre></div>
|
||||
<p>The test file keep growing longer and longer until I feel sick about it.</p>
|
||||
<p>See <a href="https://github.com/tektoncd/pipeline/blob/main/pkg/pod/pod_test.go">tektoncd/pipeline unit test</a> to get a feeling about this.
|
||||
When I see it, <code>TestPodBuild</code> has almost 2000 lines.</p>
|
||||
<p>The solution I propose here is simple (absolutely not perfect, but good with my usecase) thanks to <strong>stretchr/testify</strong>.
|
||||
I init all <strong>default</strong> action on <strong>success</strong> case.
|
||||
Then I <strong>alter</strong> request or mock error for unit test to hit on other case.
|
||||
Remember if unit test is hit, code coverate is surely increaesed, and that my <strong>goal</strong>.</p>
|
||||
<div class="highlight highlight-source-go"><pre><span class="pl-c">// Init ServiceSuite as above</span>
|
||||
}</pre>
|
||||
</div>
|
||||
<p>
|
||||
The test file keep growing longer and longer until I feel sick about it.
|
||||
</p>
|
||||
<p>
|
||||
See
|
||||
<a
|
||||
href="https://github.com/tektoncd/pipeline/blob/main/pkg/pod/pod_test.go"
|
||||
>tektoncd/pipeline unit test</a
|
||||
>
|
||||
to get a feeling about this. When I see it, <code>TestPodBuild</code> has
|
||||
almost 2000 lines.
|
||||
</p>
|
||||
<p>
|
||||
The solution I propose here is simple (absolutely not perfect, but good
|
||||
with my usecase) thanks to <strong>stretchr/testify</strong>. I init all
|
||||
<strong>default</strong> action on <strong>success</strong> case. Then I
|
||||
<strong>alter</strong> request or mock error for unit test to hit on other
|
||||
case. Remember if unit test is hit, code coverate is surely increaesed,
|
||||
and that my <strong>goal</strong>.
|
||||
</p>
|
||||
<div class="highlight highlight-source-go">
|
||||
<pre><span class="pl-c">// Init ServiceSuite as above</span>
|
||||
|
||||
<span class="pl-k">func</span> (<span class="pl-s1">s</span> <span class="pl-c1">*</span><span class="pl-smi">ServiceSuite</span>) <span class="pl-en">TestUpload</span>() {
|
||||
<span class="pl-c">// Init success request</span>
|
||||
|
@ -271,11 +350,25 @@ Remember if unit test is hit, code coverate is surely increaesed, and that my <s
|
|||
})
|
||||
|
||||
<span class="pl-c">// ...</span>
|
||||
}</pre></div>
|
||||
<p>If you think this is not quick enough, just <strong>ignore</strong> the response.
|
||||
You only need to check error or not if you want code coverage only.</p>
|
||||
<p>So if request change fields or more dependencies, I need to update success case, and maybe add corresponding error case if need.</p>
|
||||
<p>Same idea but still with table, you can find here <a href="https://arslan.io/2022/12/04/functional-table-driven-tests-in-go/" rel="nofollow">Functional table-driven tests in Go - Fatih Arslan</a>.</p>
|
||||
}</pre>
|
||||
</div>
|
||||
<p>
|
||||
If you think this is not quick enough, just <strong>ignore</strong> the
|
||||
response. You only need to check error or not if you want code coverage
|
||||
only.
|
||||
</p>
|
||||
<p>
|
||||
So if request change fields or more dependencies, I need to update success
|
||||
case, and maybe add corresponding error case if need.
|
||||
</p>
|
||||
<p>
|
||||
Same idea but still with table, you can find here
|
||||
<a
|
||||
href="https://arslan.io/2022/12/04/functional-table-driven-tests-in-go/"
|
||||
rel="nofollow"
|
||||
>Functional table-driven tests in Go - Fatih Arslan</a
|
||||
>.
|
||||
</p>
|
||||
|
||||
<div>
|
||||
Feel free to ask me via
|
||||
|
|
|
@ -43,21 +43,27 @@
|
|||
<div>
|
||||
<a href="index">Index</a>
|
||||
</div>
|
||||
<h1><a id="user-content-index" class="anchor" aria-hidden="true" href="#index"><span aria-hidden="true" class="octicon octicon-link"></span></a>Index</h1>
|
||||
<p>This is where I dump my thoughts.</p>
|
||||
<ul>
|
||||
<li><a href="2022-06-08-backup">Backup my way</a></li>
|
||||
<li><a href="2022-06-08-dockerfile-go">Dockerfile for Go</a></li>
|
||||
<li><a href="2022-07-10-bootstrap-go">Bootstrap Go</a></li>
|
||||
<li><a href="2022-07-12-uuid-or-else">UUID or else</a></li>
|
||||
<li><a href="2022-07-19-migrate-to-buf">Migrate to buf</a></li>
|
||||
<li><a href="2022-07-31-sql">SQL</a></li>
|
||||
<li><a href="2022-07-31-experiment-go">Experiment go</a></li>
|
||||
<li><a href="2022-08-10-gitignore">gitignore</a></li>
|
||||
<li><a href="2022-10-26-reload-config">Reload config</a></li>
|
||||
<li><a href="2022-12-25-archlinux">Install Arch Linux</a></li>
|
||||
<li><a href="2022-12-25-go-test-asap">Speed up writing Go test ASAP</a></li>
|
||||
</ul>
|
||||
<h1>
|
||||
<a id="user-content-index" class="anchor" aria-hidden="true" href="#index"
|
||||
><span aria-hidden="true" class="octicon octicon-link"></span></a
|
||||
>Index
|
||||
</h1>
|
||||
<p>This is where I dump my thoughts.</p>
|
||||
<ul>
|
||||
<li><a href="2022-06-08-backup">Backup my way</a></li>
|
||||
<li><a href="2022-06-08-dockerfile-go">Dockerfile for Go</a></li>
|
||||
<li><a href="2022-07-10-bootstrap-go">Bootstrap Go</a></li>
|
||||
<li><a href="2022-07-12-uuid-or-else">UUID or else</a></li>
|
||||
<li><a href="2022-07-19-migrate-to-buf">Migrate to buf</a></li>
|
||||
<li><a href="2022-07-31-sql">SQL</a></li>
|
||||
<li><a href="2022-07-31-experiment-go">Experiment go</a></li>
|
||||
<li><a href="2022-08-10-gitignore">gitignore</a></li>
|
||||
<li><a href="2022-10-26-reload-config">Reload config</a></li>
|
||||
<li><a href="2022-12-25-archlinux">Install Arch Linux</a></li>
|
||||
<li>
|
||||
<a href="2022-12-25-go-test-asap">Speed up writing Go test ASAP</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
Feel free to ask me via
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"devDependencies": {
|
||||
"prettier": "2.8.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
prettier@2.8.1:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.1.tgz#4e1fd11c34e2421bc1da9aea9bd8127cd0a35efc"
|
||||
integrity sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==
|
Loading…
Reference in New Issue