refactor: re-organise file structure

This commit is contained in:
2026-05-23 07:55:18 +08:00
parent 54ecd30a98
commit 453f20c902
50 changed files with 7 additions and 10 deletions
@@ -0,0 +1,31 @@
---
title: Cooking Seasoning Guide
tags:
- cooking
- cheatsheet
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## When to Add Seasonings
| Seasoning Type | Timing | Suitable Dishes | Effect |
|----------------------------------------|----------------------------------------|------------------------------------------------------|-------------------------------------------------------------------------------|
| **Salt** | Last | Leafy greens | Prevents leaves from wilting and releasing water, keeps them crisp and tender |
| | Mid-way | Shredded potatoes, green beans, garlic shoots | Better flavour absorption |
| **MSG, Light Soy Sauce, Oyster Sauce** | Towards the end | Dishes needing umami and aroma enhancement | Adds umami and aroma; high heat destroys freshness and fragrance |
| **Cooking Wine** | Together with ingredients | Ingredients needing deodorisation | Better deodorisation when blanching |
| | At highest wok heat | Stir-fried ingredients needing deodorisation | High heat accelerates alcohol evaporation, effective aroma searing |
| **Vinegar** | Early | Hot and sour shredded potatoes, hot and sour cabbage | Makes shredded potatoes crisper and more flavourful |
| | Drizzle around wok edge before serving | Stir-fried chilli pork and similar high-heat dishes | High-heat searing enhances aroma |
| **Dark Soy Sauce** | After half-cooked | Dishes needing colour | Achieves optimal colouring effect |
## Quick Reference
| Seasoning | Timing | Effect | Example |
|-----------------------------------------------|------------------------------|-----------------------------------------------------------------------------|---------------------------------------------------------------------------|
| **Salt** | 1015 seconds before serving | Prevents premature water loss and wilting of leafy greens; keeps them crisp | Sprinkle salt and toss before turning off the heat for lettuce or spinach |
| **Minced Garlic / Ginger** | First, after oil is hot | Releases fragrance and enhances flavour | Sauté minced garlic in hot oil, then add greens |
| **Light Soy Sauce / Oyster Sauce** (optional) | After salt | Adds umami; use sparingly to avoid overpowering the vegetables | Add 12 drops of light soy sauce after salting Shanghai greens |
| **Cooking Oil** | Hot wok, hot oil | Quick stir-fry at high heat locks in moisture | Use slightly more oil and high heat for fast stir-frying |
@@ -0,0 +1,18 @@
---
title: Docker Deployment Standards
tags:
- docker
- deployment
- standards
- best-practice
author:
name: Zihlu Wang
email: real@zihluwang.me
---
- **Dockerfiles**: Provide a `Dockerfile` for the application to enable containerised deployment.
- **Lightweight Images**: Strive for lightweight Docker images by using appropriate base images and multi-stage builds.
- **Configuration**: Ensure environment-specific configuration (e.g., database connection strings, external service
URLs) is managed through environment variables injected into Docker containers.
- **Logging**: Configure containerised logging to output to `stdout` and `stderr` so that log aggregation systems can
collect logs easily.
@@ -0,0 +1,55 @@
---
title: ECMAScript 2025 Syntax Sugar Guide
tags:
- javascript
- ecmascript
- pattern-matching
- frontend
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## Pattern Matching
### Traditional Approach
```javascript
function processResponse(response) {
if (response.status === 200 && response.data) {
return { success: true, data: response.data };
} else if (response.status === 404) {
return { success: false, error: 'Not found' };
} else if (response.status >= 500) {
return { success: false, error: 'Server error' };
} else {
return { success: false, error: 'Unknown error' };
}
}
```
### Pattern Matching Approach
```javascript
function processResponse(response) {
return match (response) {
when ({ status: 200, data }) -> ({ success: true, data })
when ({ status: 404 }) -> ({ success: false, error: 'Not found' })
when ({ status: status if status >= 500 }) -> ({ success: false, error: 'Server error' })
default -> ({ success: false, error: 'Unknown error' })
};
}
```
### Handling Array Length Branches Gracefully
```javascript
function handleArray(arr) {
return match (arr) {
when ([]) -> "Empty array"
when ([first]) -> `Only one element: ${first}`
when ([first, second]) -> `Two elements: ${first}, ${second}`
when ([first, ...rest]) -> `First element: ${first}, others: ${rest.length} items`
};
}
```
+43
View File
@@ -0,0 +1,43 @@
---
title: Email Like a Boss
tags:
- communication
- email
- soft-skill
- career
- productivity
author:
name: Zihlu Wang
email: real@zihluwang.me
---
Email at work is about more than sharing information — it's about building trust and shaping your professional image.
The same message, phrased differently, can leave an entirely different impression.
Below are 9 common email scenarios, contrasting "low-power" expressions with "high-power" alternatives that make you
sound more confident and professional.
## Scenario Cheatsheet
| Scenario | ❌ Don't Use (sounds…) | ✅ Use Instead (sounds…) | Why It Works |
|----------------------------|----------------------------------------------|-----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Late reply | **Sorry for the delay** | **Thanks for your patience** | Swaps an apology for gratitude — the focus shifts from "I was wrong" to "you were generous," acknowledging the delay while making the recipient feel respected |
| Scheduling | **What works best for you?** | **Could you do …?** | The former throws the decision entirely back to the other person; the latter offers a concrete option, cutting down on back-and-forth |
| After helping someone | **No problem / No worries** | **Always happy to help** | The former implies the task *could* have been a problem; the latter signals you enjoyed it and would happily do it again |
| Making a suggestion | **I think maybe we should …** | **It'd be best if we …** | The former oozes hesitation and self-doubt; the latter delivers a clear judgment — like someone with experience making a decision |
| Text isn't working | **_Spending 30 minutes rewriting an email_** | **It'd be easier to discuss in person** | Recognising the medium itself is the bottleneck and switching channels can be the most efficient move |
| Checking for understanding | **Hopefully that makes sense?** | **Let me know if you have questions** | The former betrays doubt about your own clarity; the latter calmly shares responsibility — the reader now has an action item too |
| Following up on progress | **Just wanted to check in** | **When can I expect an update?** | The former tiptoes around the ask; the latter names the time frame directly — clear, polite, and professional |
| Owned a small mistake | **Ahh sorry my bad totally missed that** | **Thanks for letting me know** | Over-apologising makes things awkward; this acknowledges the catch while keeping the focus on moving forward |
| Need to leave early | **Could I possibly leave early?** | **I will need to leave at …** | The former asks for permission; the latter states a plan — you're a professional and don't need to apologise for reasonable needs |
## Core Principles
Writing great emails is less about vocabulary and more about **stance**. Keep three rules in mind:
1. **State instead of ask** — "I need…" carries more weight than "Could I possibly…"
2. **Thank instead of apologise** — Shift the focus from "my shortcoming" to "their support"
3. **Be specific instead of vague** — Offer exact times, options, and action items rather than lobbing the ball back
into their court
Next time you open your inbox, take five seconds to ask: can I phrase this more like someone who makes decisions?
@@ -0,0 +1,40 @@
---
title: Fix macOS Monterey+ Devices Waking Frequently from Sleep
tags:
- macos
- sleep
- power-management
- bug
---
> This article was originally written by **落格博客
**: [落格博客](https://www.logcg.com/) » [Frequent Wake-from-Sleep Issues After Upgrading to macOS Monterey](https://www.logcg.com/archives/3528.html)
After upgrading to macOS Monterey, my screen kept lighting up in the middle of the night for no apparent reason. It had
happened before, but only when notifications came in. Now the screen lights up on its own with no trigger — same
hardware, so it must be a software issue.
After searching online, I first
found [Apple's official guide](https://support.apple.com/en-gb/guide/mac-help/mchlp2995/mac). It's very detailed, but
clearly of no help whatsoever.
Digging deeper, I found the root cause. Run **`pmset -g log | grep DarkWake`** and you'll see your Mac hasn't been
resting while you slept...
Several typical patterns appear, most with a DarkWake immediately followed by a Wake. The issue: DarkWake is meant to
wake the computer in the background to update data, but somehow a peripheral gets triggered, causing a full system wake.
In any case, I don't want this feature — Power Nap. For me, I'd rather it save as much power as possible. The fix path:
disable network access wake, disable Power Nap... But here's the catch — on M1 devices, there is no Power Nap option in
settings. (Clearly, Apple is confident in their battery life but overlooked the power of bugs.)
So for Power Nap, we have to go through the command line. First, check the current status with **`pmset -g`**. Find the
**`powernap`** value — if it isn't 0, it's enabled. Disable it with **`sudo pmset -a powernap 0`**.
Also check **`tcpkeepalive`** — it likely isn't 0 either and should also be turned off. It controls whether your Mac
maintains TCP connections while sleeping. Run **`sudo pmset -a tcpkeepalive 0`** — you'll see a terminal warning:
***Warning: This option disables TCP Keep Alive mechanism when system is sleeping. This will result in some critical
features like 'Find My Mac' not to function properly.***
Essentially, turning it off limits some features — the system simply won't connect to the network while asleep. I'm
fairly confident that if someone actually steals your Mac, they won't be getting it online anyway.
@@ -0,0 +1,30 @@
---
title: Font Size Conversion Table
tags:
- typography
- font
- cheatsheet
- design
author:
name: Zihlu Wang
email: real@zihluwang.me
---
| Chinese Size Name | English Point Size (pt) | mm | px |
|--------------------|-------------------------|-------|------|
| 初号 (Primary) | 42 | 14.82 | 56 |
| 小初 (Small Primary) | 36 | 12.7 | 48 |
| 一号 (No. 1) | 26 | 9.17 | 34.7 |
| 小一 (Small No. 1) | 24 | 8.47 | 32 |
| 二号 (No. 2) | 22 | 7.76 | 29.3 |
| 小二 (Small No. 2) | 18 | 6.35 | 24 |
| 三号 (No. 3) | 16 | 5.64 | 21.3 |
| 小三 (Small No. 3) | 15 | 5.29 | 20 |
| 四号 (No. 4) | 14 | 4.94 | 18.7 |
| 小四 (Small No. 4) | 12 | 4.23 | 16 |
| 五号 (No. 5) | 10.5 | 3.7 | 14 |
| 小五 (Small No. 5) | 9 | 3.18 | 12 |
| 六号 (No. 6) | 7.5 | 2.56 | 10 |
| 小六 (Small No. 6) | 6.5 | 2.29 | 8.7 |
| 七号 (No. 7) | 5.5 | 1.94 | 7.3 |
| 八号 (No. 8) | 5 | 1.76 | 6.7 |
@@ -0,0 +1,47 @@
---
title: Frontend Development Standards
tags:
- frontend
- react
- standards
- best-practice
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## Dependency Management (pnpm)
- **Strictness**: Leverage pnpm's strict dependency management to ensure a more deterministic `node_modules` structure
and efficient disk space usage.
- **Workspaces**: When using a monorepo approach, configure pnpm workspaces to streamline dependency management across
multiple frontend packages.
- **Auditing**: Regularly audit frontend dependencies for known vulnerabilities using `pnpm audit`.
## API Communication (Axios)
- **Axios Instance**: Create a centralised Axios instance for API calls to apply common configuration (base URL,
headers, interceptors).
- **Interceptors**: Use Axios interceptors to:
- Add authentication tokens to outgoing requests.
- Handle global error responses (e.g., display a notification for `401 Unauthorized`).
- Log requests/responses in development environments.
- **Error Handling**: Centralise API error handling in Axios interceptors or custom utility functions to provide
consistent user feedback.
## React and Component Standards
- **Function Components & Hooks**: Prefer function components with React Hooks over class components for new
development.
- **Props**:
- Define `interface` or `type` for component props to ensure type safety.
- Destructure props at the component entry point for clarity.
- **State Management (Redux)**:
- Use Redux Toolkit for efficient, boilerplate-reduced Redux development.
- Use `createSlice` to organise Redux logic into "slices" (feature-specific reducers, actions, and selectors).
- Follow the "ducks" pattern or "slices" approach to co-locate Redux logic.
- **Component Composition**: Break down complex UIs into smaller, reusable, single-responsibility components.
- **Ant Design**:
- Leverage Ant Design components for consistent UI/UX.
- Use CSS-in-JS solutions to consistently customise Ant Design themes and styles across the application if needed.
- **Accessibility**: Design and implement components with web accessibility (a11y) in mind from the start.
@@ -0,0 +1,75 @@
---
title: Frontend Tips and Solutions Cheatsheet
tags:
- frontend
- react
- ant-design
- tailwind
- best-practice
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## Decoupling Form Components
Split form components into UI components and logic components. Use the UI components inside the logic components for rendering styles.
## React Entry Component Order
```typescript
import { StrictMode } from "react"
import { createRoot } from "react-dom/client"
import { Provider as ReduxProvider } from "react-redux"
import { PersistGate } from "redux-persist/integration/react"
import { RouterProvider } from "react-router"
import { App as AntApp, ConfigProvider as AntConfigProvider } from "antd"
import { StyleProvider } from "@ant-design/cssinjs"
import AntSimplifiedChinese from "antd/locale/zh_CN"
import "./index.css"
import "@/config/dayjs-config"
import store, { persistor } from "@/store"
import router from "@/router"
createRoot(document.getElementById("root")!).render(
<StrictMode>
<ReduxProvider store={store}>
<PersistGate loading={null} persistor={persistor}>
{/* Note: StyleProvider must be the parent of ConfigProvider!!! */}
<StyleProvider layer>
<AntConfigProvider
locale={AntSimplifiedChinese}
button={{
autoInsertSpace: false,
}}>
<AntApp className="h-full w-full">
<RouterProvider router={router} />
</AntApp>
</AntConfigProvider>
</StyleProvider>
</PersistGate>
</ReduxProvider>
</StrictMode>
)
```
## Integrating Ant Design with Tailwind CSS in React
> Reference: [https://github.com/ant-design/ant-design/discussions/56152](https://github.com/ant-design/ant-design/discussions/56152)
```css
/* index.css */
@layer theme, base, antd, components, utilities;
@import "../../../node_modules/.pnpm/tailwindcss@4.3.0/node_modules/tailwindcss/dist/lib.d.mts";
```
```typescript
<StyleProvider>
<ConfigProvider>
<App>
<RouterProvider/>
</App>
</ConfigProvider>
</StyleProvider>
```
@@ -0,0 +1,26 @@
---
title: General Application Development Standards
tags:
- standards
- best-practice
- engineering
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## Introduction
This document outlines coding standards and best practices for application development. Adhering to these guidelines
ensures code quality, maintainability, and consistency across all projects.
## General Principles
- **Clarity & Readability**: Code must be easy to read and understand. Prefer clear, self-documenting code over clever,
terse, but obscure solutions.
- **Consistency**: Maintain a consistent coding style, naming conventions, and architectural patterns across the
project.
- **Modularity**: Design components with loose coupling and high cohesion to promote reusability and simpler testing.
- **Testability**: Write code that is inherently testable.
- **Security-First Design**: Incorporate security considerations at every stage of development.
- **Performance Awareness**: Be mindful of performance implications for critical code paths and API endpoints.
+60
View File
@@ -0,0 +1,60 @@
---
title: GitLab Operations
tags:
- gitlab
- devops
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## Setting the Default Language to Chinese in GitLab
GitLab filters out languages with less than 90% translation coverage via the `SWITCHER_MINIMUM_TRANSLATION_LEVEL`
variable in the code. Simplified Chinese sits at 84% and Traditional Chinese at 83%.
Navigate to `/path/to/gitlab/embedded/service/gitlab-rails/app/helpers/preferred_language_switcher_helper.rb` and modify
the code as follows:
```diff title="/path/to/gitlab/embedded/service/gitlab-rails/app/helpers/preferred_language_switcher_helper.rb"
- SWITCHER_MINIMUM_TRANSLATION_LEVEL = 90
+ SWITCHER_MINIMUM_TRANSLATION_LEVEL = 80
```
Then restart GitLab with `gitlab-ctl restart`.
## Adding ICP Filing Information to the Homepage
Navigate to `/path/to/gitlab/embedded/service/gitlab-rails/app/views/devise/shared/_footer.html.haml` and add the
following:
```diff
+ = link_to _("Your ICP Filing Number"), "https://beian.miit.gov.cn/", target: '_blank', class: 'text-nowrap', rel: 'noopener noreferrer'
```
## Upgrading GitLab
### Downloading the GitLab CE Package
Download the deb package from the [GitLab Package page](https://packages.gitlab.com/gitlab/gitlab-ce) and transfer it to
the server using `scp` or a similar tool.
### Stopping Memory-Intensive Services
```shell
gitlab-ctl stop puma
gitlab-ctl stop sidekiq
```
### Running a Backup
```shell
# Example: skip build artefacts and container registry, backing up only the database and repositories
sudo gitlab-backup create SKIP=artifacts,registry
```
### Installing
```shell
sudo dpkg -i gitlab-package.deb
```
@@ -0,0 +1,131 @@
---
title: Google Code Review Standards
tags:
- code-review
- best-practice
- engineering
- google
author:
name: Zihlu Wang
email: real@zihluwang.me
---
Code review is a step in the development process where one or more developers review code written by another developer (
the author) to ensure that:
- The code is free of errors, bugs, and issues;
- The code conforms to quality and style guide requirements and standards;
- The code fulfils all intended functionality;
- Once merged, the codebase continues to function properly and is left in a better state.
This is why code review is a vital part of software development. Code reviewers act as gatekeepers, responsible for
deciding whether the code is ready to become part of the codebase and enter production.
## The Code Should Improve the Overall Health of the System
Every code change (pull request) should improve the overall health of the system. The key point is that even small
improvements, once merged, enhance the health of the software or codebase.
## Review Code Quickly and Provide Active Responses and Feedback
First and foremost, do not delay the merging of code. There is no such thing as perfect code. If the code improves the
overall health of the system, it should be delivered promptly.
> The key insight is that there is no perfect code — only better code. — Google Engineering Practices Documentation
If there are no urgent tasks at hand, review code as soon as it is submitted. The maximum response time for a pull
request should not exceed one working day. Multiple rounds of partial or complete code review should be completed for a
single pull request within a day.
## Educate and Inspire During Code Review
During code review, provide guidance by sharing knowledge and experience wherever possible.
## Review Code Against Standards
Always remember that style guides, coding standards, and relevant documentation should serve as the absolute authority
in code review. For example, when consistency of tabs versus spaces is in question, cite the coding conventions.
> If you use Java, the following article may be helpful — it summarises Java coding best practices from major tech
> companies: [A Short Summary of Java Coding Best Practices](https://rhamedy.medium.com/a-short-summary-of-java-coding-best-practices-31283d0167d3)
## Resolving Code Review Conflicts
When resolving conflicts during code review, follow the agreed best practices in the style guide and coding standards,
and seek advice from others with more domain knowledge and experience.
If your comment is optional or relatively unimportant, indicate so in the comment and let the author decide whether to
address or skip it.
As a reviewer, when there is no style guide or coding standard to reference, you can at minimum suggest that the code
change remain consistent with the rest of the codebase.
## UI Change Demonstrations Are Part of Code Review
If a code change involves user interface modifications, a demonstration is required in addition to the code review to
ensure the UI meets expectations and aligns with the interface design.
For frontend code changes, you must provide a demonstration or ensure that the code change includes the necessary UI
automation tests to verify the added or updated functionality.
## Ensure All Tests Are Included in the Code Review
Unless in an emergency, pull requests should include all necessary tests, such as unit tests, integration tests, and
end-to-end tests.
An emergency here means a bug or security vulnerability that needs fixing as soon as possible, and tests can be added
later. In such cases, ensure that appropriate tickets/issues are created and someone is responsible for completing the
tests immediately after the hotfix or deployment.
Skipping tests must never be accepted. If time is short and certain goals risk not being met, the solution is not to
skip tests but to scope down the deliverables.
## Don't Interrupt Your Own Work for Code Review
If you are deeply focused on your work, don't interrupt yourself — it takes a long time to get back into the flow. In
other words, the cost of interrupting a developer in flow far exceeds the cost of making them wait for a code review. Do
your code reviews after a break (lunch, coffee, etc.).
Most of the time, an entire code review and merge cannot be completed in a single day. What matters is giving the author
prompt feedback. For example, even if you cannot complete a full review, you can quickly point out a few areas worth
discussing. This significantly reduces frustration during the review process.
## Review All Code — Make No Assumptions
Review every line of code that is submitted. Do not make assumptions about manually written classes and methods, and
make sure you understand what the code is doing.
Make sure you understand the code you are reviewing. If you don't, ask the author for clarification or a code
walkthrough and explanation. If you are not qualified to review part of the code, ask another qualified developer to
review it in your place.
## Keep the Big Picture in Mind During Code Review
It helps to look at code changes from a broader perspective. For example, a file is modified and four new lines of code
are added. Don't just look at those four lines — consider reviewing the entire file and examining what was added. Do the
additions degrade the quality of existing code? Do they make existing functionality a candidate for refactoring?
Reviewing added code outside the context of the function/method or class will, over time, lead to classes that are
unmaintainable, tangled, difficult to test, and hard to extend or refactor.
Remember that just as trivial improvements can compound into a better product over time, even minor code degradation or
technical debt can accumulate to the point where the product becomes difficult to maintain and extend.
## Recognise and Encourage Great Work During Code Review
If you see an excellent code change, don't forget to acknowledge and encourage the author generously. The purpose of
code review is not only to find errors but also to encourage and guide developers towards great work.
## Be Considerate, Respectful, Kind, and Clear During Code Review
It is critical to remain kind, clear, polite, and respectful during code review, while also providing the author with
clear feedback and positive assistance. When reviewing code, comment on the code — not the developer.
## Explain Code Review Comments Thoroughly, with a Sense of Proportion
Whenever a code review comment proposes an alternative or points out a problem, it's important to explain the reasoning
and provide examples based on your knowledge and experience to help the developer understand why your suggestion
improves the code quality.
When suggesting modifications or changes, find the right balance in how you guide the author. For example, I prefer
guidance, explanation, hints, or suggestions over providing the entire solution.
+18
View File
@@ -0,0 +1,18 @@
---
title: Blog
---
# Blog
Welcome to the OnixByte blog — a collection of practical guides, cheatsheets, and standards
distilled from daily engineering work.
## Topics
- **Frontend** — React patterns, TailwindCSS tips, ECMAScript syntax guides, browser quirks
- **Backend & Database** — Java development, MyBatis, PostgreSQL, MySQL JSON
- **DevOps & Infrastructure** — Docker, GitLab CI/CD, LDAP, MinIO, macOS troubleshooting
- **Engineering Standards** — code review, version control, general and language-specific conventions
- **Tools & Productivity** — email etiquette, font sizing, data analysis methods
Browse the sidebar or scroll through the posts below — each article is self-contained and written to be immediately useful.
@@ -0,0 +1,210 @@
---
title: Java Development Cheatsheet
tags:
- java
- tips
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## Comparison for `BigDecimal`
In Java, comparing `BigDecimal` values requires care because `equals` and `compareTo` behave differently.
### `equals` vs `compareTo`
`BigDecimal#equals` checks both **value** and **scale**, while `BigDecimal#compareTo` only checks **value** (ignoring scale). This means:
```java
var a = new BigDecimal("100"); // scale = 0
var b = new BigDecimal("100.00"); // scale = 2
a.equals(b); // false — different scale
a.compareTo(b); // 0 — same mathematical value
var c = new BigDecimal("200");
a.compareTo(c); // -1 (negative) — a is less than c
c.compareTo(a); // 1 (positive) — c is greater than a
```
### Why this matters
The scale mismatch often appears when values come from different sources — e.g., parsing user input, reading from a database (`DECIMAL(10,2)` columns), or receiving JSON payloads. You might think two values are equal when `equals` says they aren't.
### Rule of thumb
- Use **`compareTo`** for numeric equality checks: `a.compareTo(b) == 0`
- Use **`equals`** only when you mean "identical representation" (same value and same scale)
- Use **`stripTrailingZeros()`** if you need to normalise scale before `equals`:
```java
a.stripTrailingZeros().equals(b.stripTrailingZeros()); // true
```
### Comparing with zero
Avoid `==` or `.equals(BigDecimal.ZERO)` to check for zero — prefer `compareTo`:
```java
if (value.compareTo(BigDecimal.ZERO) == 0) { ... }
```
## How to Retrieve Data from a BlockingQueue
- `take()` — retrieves and removes the head of the queue, waiting if necessary until an element becomes available.
- `poll()` — retrieves and removes the head of the queue, or returns `null` if the queue is empty.
- `poll(long timeout, TimeUnit unit)` — retrieves and removes the head of the queue, waiting up to the specified wait time if necessary for an element to become available. Returns `null` if the timeout expires.
- `peek()` — retrieves but does not remove the head of the queue. Returns `null` if the queue is empty.
## Spring Cloud Alibaba FAQs
### How to prevent Nacos from creating a `nacos` folder in the user's home directory?
Add the following two configuration properties to specify the Nacos storage path:
- `JM.LOG.PATH`
- `JM.SNAPSHOT.PATH`
### How to deal with Sentinel's scattered log files?
Add the configuration property `csp.sentinel.log.dir` to change Sentinel's log directory.
### How to add Configuration Properties in JetBrains IntelliJ IDEA?
In JetBrains IntelliJ IDEA, click **`Edit Configurations…`** in the run configuration dropdown at the top right.
Click the **`Modify options`** button on the page, then add the properties you need to reset in the **`Override configuration properties`** table that appears below.
## Spring Data JPA FAQs
### How to fix the "Serializing `PageImpl` instances as-is not supported" warning?
Spring Data JPA warns about unstable JSON serialization of `PageImpl`. To resolve this, enable VIA_DTO serialization mode on your application's main class:
```java
@EnableSpringDataWebSupport(pageSerializationMode =
EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO)
```
### Why does the first page return no results?
JPA pagination is **zero-indexed**. Page `0` is the first page. If your frontend sends `page=1`, you need to pass `page - 1` to Spring Data:
```java
Pageable pageable = PageRequest.of(requestPage - 1, pageSize);
```
### How to avoid the N+1 query problem?
The N+1 problem occurs when JPA executes one query for the parent entity, then N additional queries for each child association.
**Detection** — look for repetitive SQL queries in the logs, or configure `spring.jpa.properties.hibernate.generate_statistics=true` to spot it.
**Fixes:**
| Approach | When to use |
|--------------------------|---------------------------------------------------|
| `@EntityGraph` | Declarative, good for entity-specific fetch plans |
| `JOIN FETCH` in `@Query` | Fine-grained control per query |
| `@BatchSize` | Reduces N+1 to N/k+1 by batching |
```java
// Option 1: EntityGraph
@EntityGraph(attributePaths = {"roles", "permissions"})
Optional<User> findById(long id);
// Option 2: JOIN FETCH
@Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.id = :id")
Optional<User> findByIdWithRoles(@Param("id") long id);
```
### `findById` vs `getReferenceById` — which one to use?
- **`findById`** — hits the database immediately, returns the entity or `Optional.empty()`. Use this when you need the data.
- **`getReferenceById`** — returns a lazy proxy **without** hitting the database. Throws `EntityNotFoundException` only when you access a non-existent proxy's properties. Use this when you only need the ID to set a foreign key relationship.
```java
// Good: only need the user reference to set a FK
Post post = new Post();
post.setAuthor(userRepository.getReferenceById(userId));
```
### How to fix `LazyInitializationException`?
This happens when you access a lazily-loaded association outside the persistence context (e.g., in a controller or serializer after the transaction has closed).
**Solutions:**
1. **Use `JOIN FETCH` or `@EntityGraph`** to eagerly load needed associations.
2. **Use DTO projections** — return only the fields you need instead of whole entities:
```java
@Query("SELECT new com.example.UserDto(u.id, u.name) FROM User u WHERE u.id = :id")
UserDto findUserDtoById(@Param("id") long id);
```
3. **`@Transactional(readOnly = true)`** on the service method — keep the session open for the entire method scope.
### When should I use `@Transactional(readOnly = true)`?
Use `@Transactional(readOnly = true)` on **read-only** service methods for three benefits:
- Hibernate skips dirty checking (no snapshots, less memory).
- The JDBC driver may route to read replicas.
- It documents the intent clearly.
```java
@Service
public class UserService {
@Transactional(readOnly = true)
public UserDto getUser(long id) { ... }
@Transactional
public UserDto createUser(CreateUserRequest request) { ... }
}
```
### `save()` vs `saveAll()` — which is faster for batch inserts?
`saveAll()` uses a single transaction and can benefit from JDBC batching. Configure the batch size:
```yaml
spring:
jpa:
properties:
hibernate:
jdbc:
batch_size: 20
order_inserts: true
order_updates: true
```
For large bulk inserts (thousands of rows), consider `JdbcTemplate` batch operations instead — Hibernate's entity management overhead is significant at that scale.
### How to use dynamic queries with `Specification`?
For complex search forms with optional filters, use `JpaSpecificationExecutor`:
```java
public interface UserRepository extends JpaRepository<User, Long>,
JpaSpecificationExecutor<User> {
}
// Usage
Specification<User> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (name != null) {
predicates.add(cb.like(root.get("name"), "%" + name + "%"));
}
if (status != null) {
predicates.add(cb.equal(root.get("status"), status));
}
return cb.and(predicates.toArray(new Predicate[0]));
};
Page<User> page = userRepository.findAll(spec, pageable);
```
@@ -0,0 +1,239 @@
---
title: Java Development Standards
tags:
- java
- spring-boot
- standards
- best-practice
- backend
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## Java Language and Coding Style
- **Java Version**: Projects should use the latest LTS version of the JDK whenever possible.
- **Naming Conventions**:
- Classes: `PascalCase` (e.g., `UserService`, `OrderController`).
- Methods: `camelCase` (e.g., `getUserById`, `saveOrder`).
- Variables: `camelCase` (e.g., `username`, `statusCode`).
- Constants: `SCREAMING_SNAKE_CASE` (e.g., `DEFAULT_PAGE_SIZE`).
- **Immutability**: Prefer immutability for domain objects and DTOs where possible, using `records` or immutable classes to reduce side effects and improve thread safety.
- **Optional**: Use `Optional<T>` to explicitly handle potentially absent values and avoid `NullPointerException`.
- **Streams API**: Prefer the Java Streams API for collection processing, promoting functional and declarative programming.
- **Exception Handling**:
- Use specific exceptions. Avoid catching generic `Exception`.
- Throw runtime exceptions for unrecoverable errors.
- Define custom checked exceptions for recoverable errors if required by business logic.
- Leverage Spring's `@ControllerAdvice` and `@ExceptionHandler` for centralised global exception handling and consistent API error responses.
- **Code Review**: All backend code must undergo thorough manual code review before merging, particularly following GitFlow principles. IntelliJ IDEA's integrated code analysis tools should be used as a first-pass review.
## Documentation and Comments
- All **public** classes, methods, and significant fields in backend Java code must include comprehensive Javadoc comments.
- Javadoc should explain the purpose, parameters (`@param`), return values (`@return`), and thrown exceptions (`@throws`).
- Javadoc formatting:
- Javadoc must follow a maximum of 100 characters per line (including whitespace for formatting). If the content exceeds 100 characters, break at the last word that ends within the 100-character limit. However, if after the line break only a single word remains at the start of the next line, break one word earlier.
```java
/**
* Enables configuration properties for S3 file storage services. Individual service beans are
* created by their respective service classes to better support conditional configuration.
*/
```
- Use `<p>` to separate paragraphs.
```java
/**
* This is the first paragraph of the Javadoc.
* <p>
* This is the second paragraph of the Javadoc.
*/
```
- Each paragraph must be a grammatically correct and semantically complete paragraph following English sentence conventions.
- All `@param`, `@return`, `@throws`, and `@see` explanations must follow these rules:
- Do not start with a capital letter.
- If the description ends with a declarative sentence, do not use punctuation at the end.
```java
/**
* Returns the greater of two {@code int} values. That is, the
* result is the argument closer to the value of
* {@link Integer#MAX_VALUE}. If the arguments have the same value,
* the result is that same value.
*
* @param a an argument
* @param b another argument
* @return the larger of {@code a} and {@code b}
*/
public static int max(int a, int b) {
return (a >= b) ? a : b;
}
```
## Dependency Management (Gradle)
- **Build File**: `build.gradle.kts` must be well-organised with clear separation of plugins, dependencies, and tasks.
- **Dependency Versions**: Dependency versions must be centrally managed in the `gradle/libs.versions.toml` file to ensure consistency.
- **Plugin Management**: Explicitly declare Gradle plugins and their versions.
- **Avoid Unnecessary Dependencies**: Only include dependencies actually used by the project. Regularly review and clean up unused dependencies.
## API Design (RESTful)
- **RESTful Principles**: Follow RESTful principles:
- **Resources**: Model data as resources identifiable by URI.
- **HTTP Methods**: Use standard HTTP methods appropriately (GET for retrieval, POST for creation, PUT for full update, PATCH for partial update, DELETE for removal).
- **Statelessness**: APIs should be stateless; each request from client to server must contain all the information needed to understand the request.
- **URIs**:
- Use plural nouns for collection resources (e.g., `/users`, `/products`).
- Use hyphens in URIs for readability (e.g., `/user-accounts`).
- Avoid verbs in URIs (e.g., use `/users` instead of `/getAllUsers`).
- **Status Codes**: Use appropriate HTTP status codes to indicate the result of API requests (e.g., `200 OK`, `201 Created`, `204 No Content`, `400 Bad Request`, `401 Unauthorized`, `403 Forbidden`, `404 Not Found`, `500 Internal Server Error`).
- **Response Format**: JSON is the preferred response format.
- **Versioning**: Where version control is required, use the `X-Endpoint-Version` header parameter to control API versions.
## Spring Boot Best Practices & Layered Architecture
- **Layered Architecture (MVC with Manager Layer)**: Our backend applications follow a strict multi-layered architecture, ensuring clear separation of responsibilities and improving maintainability and testability. The layers and their responsibilities are:
- **Controller Layer**: Located in the `controller` package. Responsible for exposing RESTful APIs, handling HTTP requests, and mapping request parameters/bodies to service layer calls. Controllers should remain lightweight, focusing primarily on input validation (using DTOs) and coordinating calls to the `Service` layer.
- **Service Layer**: Located in the `service` package. This layer encapsulates core business logic. Services expose a directly consumable API to the `Controller` layer, abstracting business processes and transaction management. Services coordinate calls to the `Manager` layer to execute business operations.
- **Manager Layer**: Located in the `manager` package. This layer provides atomic business operations that can be composed by the `Service` layer. Managers typically handle more complex business logic and may involve interacting with multiple repositories or other external systems at a finer granularity.
- **Repository Layer (MyBatis)**: Located in the `repository` package and `src/main/resources/repository` (for XML mapping files). This layer is responsible for providing atomic database interaction operations.
**Inter-Layer Communication Strategy**:
- **Components within a layer may only call components in the layer directly below it.**
- **Cross-layer calls are strictly prohibited** (e.g., Controller directly calling Manager, Service directly calling Repository).
- **Upward calls are strictly prohibited** (e.g., Service calling Controller).
- **Lateral calls** (e.g., Service A calling Service B for a different domain) should be carefully considered and typically indicate a need to refactor shared logic into a `Manager` or a dedicated `Service` for that shared concern.
- **Configuration**: Prefer `application.yml` over `application.properties` for configuration properties, offering better readability and hierarchical structure. Use `@ConfigurationProperties` for type-safe configuration.
- **Dependency Injection**: All dependencies should use constructor injection (mandatory dependencies) or setter injection (optional dependencies). Avoid `@Autowired` on fields, as it makes testing more difficult and hides dependencies.
- **Services**: Business logic classes are annotated with `@Service`. Services should be kept lean and focused on orchestrating domain operations, typically involving business logic processing through `Manager` layer components and interaction through `Manager` or directly with MyBatis repositories (if the particular operation does not require intermediate Manager logic, though the Manager layer is preferred for all repository interactions consistent with the layered architecture definition).
- **Repositories (MyBatis & JPA)**:
- **The project uses both MyBatis and JPA for database operations**. Repository interfaces in the `repository` and `mapper` packages define the data access contract.
- Corresponding SQL definitions are managed in XML mapping files located in `src/main/resources/mapper`.
- Data operation conventions:
- Use JPA for simple database operations.
- Use MyBatis for complex database operations.
- When performing paginated queries, page numbers should start from 0 (*compatible with Spring Data JPA*).
- **Data Access Method Naming Conventions**:
- For **querying data lists**: methods **must** start with `selectListBy`, followed by the filter criteria (e.g., `selectListByUserId`, `selectListByDepartmentIdAndStatus`). These methods must also include a `PageRequest` parameter for pagination.
- For querying **single data records**: methods **must** start with `selectOne` (e.g., `selectOneById`, `selectOneByUsername`).
- For **saving new data**: methods **must** be named `save`, and Mapper method return type must be `int` (affected row count).
- For **updating existing data**: methods **must** be named `update`, and Mapper method return type must be `int` (affected row count).
- For **deleting data**: methods **must** start with `deleteBy`, clearly indicating the deletion criteria (e.g., `deleteById`). Mapper method return type must be `int` (affected row count).
- **Controllers**:
- Annotate REST controllers with `@RestController`.
- Use `@GetMapping`, `@PostMapping`, etc. to map HTTP methods (GET, POST, PUT, DELETE) to appropriate controller methods.
- Ensure request and response payloads are clearly defined (DTOs) and documented.
- Controllers should primarily handle HTTP request/response mapping and delegate actual business logic to the service layer.
- **DTOs (Data Transfer Objects)**: Define separate DTOs for request and response bodies to decouple the internal domain model from the API contract. Use validation annotations on DTOs (e.g., `@Valid`, `@NotNull`, `@Size`).
- **Logging (SLF4J & Logback)**:
- All logging in the application uses `org.slf4j.Logger`.
- Configure `application-dev.yml` to set logging levels, appenders, and output formats.
- Log messages should be descriptive and provide sufficient context. Avoid logging sensitive information.
- Use parameterised logging for performance and to prevent string concatenation overhead (e.g., `log.debug("Processing user: {}", userId);`).
- Standard log levels: `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`.
## Project Structure
Backend applications follow a structured Gradle project layout. The core application should be clearly divided into various sub-packages.
```text
backend-application
├── build.gradle.kts // Project's main Gradle build script
├── config // External configuration directory
│ ├── application-dev.yml // Application properties for the development environment
│ └── application-prod.yml.example // Example production properties (to be copied and configured)
├── database // Database-related files
│ └── init.d // Database initialisation scripts
│ └── init-en_GB.sql // SQL script for database schema and initial data using the specified locale (British English)
├── gradle // Gradle Wrapper and configuration files
│ ├── libs.versions.toml // Centralised dependency version management (Gradle Version Catalog)
│ └── wrapper // Gradle Wrapper files
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties // Project-specific Gradle properties
├── gradlew // Gradle Wrapper executable (Linux/macOS)
├── gradlew.bat // Gradle Wrapper executable (Windows)
├── settings.gradle.kts // Gradle settings for multi-project builds (if applicable)
└── src
├── main
│ ├── java
│ │ └── com/onixbyte/application // Application root package
│ │ ├── Application.java // Spring Boot application main entry point
│ │ ├── config // Spring configuration classes
│ │ ├── constant // Classes defining application-wide constants
│ │ ├── controller // REST API endpoints
│ │ ├── domain // Core domain models and related types
│ │ │ ├── common // Common domain objects/utilities
│ │ │ ├── entity // JPA/MyBatis entities representing database tables
│ │ │ ├── model // General-purpose entity class models
│ │ │ ├── view // Data transfer objects specifically for read-only operations (e.g., query results, report structures)
│ │ │ └── web // Data transfer objects specifically for web request/response bodies
│ │ │ ├── request // Request DTOs
│ │ │ └── response // Response DTOs
│ │ ├── exception // Custom application-specific exceptions
│ │ ├── extension // Extension points or custom functionality
│ │ │ ├── jackson // Jackson serialisation/deserialisation extensions
│ │ │ └── redis // Redis-related extensions
│ │ │ └── serializer
│ │ ├── filter // Servlet filters or Spring Security filters
│ │ ├── manager // Business logic coordinators, typically orchestrating multiple services or repositories
│ │ ├── mapper // Data access layer (MyBatis interfaces)
│ │ ├── processor // General-purpose processing components or business workflows
│ │ ├── properties // Classes for type-safe configuration properties (`@ConfigurationProperties`)
│ │ ├── repository // Data access layer (Spring Data JPA interfaces)
│ │ ├── security // Spring Security-specific components
│ │ │ ├── authentication // Custom authentication mechanisms
│ │ │ └── provider // Custom authentication providers
│ │ ├── service // Core business logic (transactional layer)
│ │ ├── utils // General-purpose utility classes
│ │ └── validation // Custom validation logic
│ │ └── group // Validation groups for different contexts (e.g., create, update)
│ └── resources
│ ├── application.yml // Default application properties
│ └── mapper // MyBatis XML mapping files
└── test
└── java
└── com/onixbyte/helix
└── HelixApplicationTests.java // Spring Boot integration tests
```
**Key Observations and Specific Instructions:**
- **External `config` Directory**
- Environment configurations (`application-dev.yml`, `application-prod.yml.example`) are managed in the top-level `config` directory, separate from `src/main/resources`. This facilitates environment-specific property management, allowing different configurations to be mounted or linked at deployment time.
- **Do not upload any configuration files other than `src/main/resources/application.yml` to the Git repository.**
- **Database Initialisation**: The `database/init.d` directory is reserved for SQL scripts, specifically database schema initialisation (`init-en_GB.sql`), which is critical for environment setup and CI/CD pipelines. This structure suggests a "schema-first" or "code-driven schema evolution" approach.
- **`client` Package**: This package is used to provide services for all middleware to the application, such as HTTP, S3 storage, Redis calls, etc. For self-coded functional implementations that need to be added to the Spring context (such as JSON Web Token generation and parsing), placing them in this package is also recommended.
- **MyBatis & Spring Data JPA Integration**
- The `src/main/java/.../mapper` package contains MyBatis mapper interfaces, while the actual SQL definitions reside in `src/main/resources/mapper/*.xml` files. This separation is key to keeping code clean while leveraging MyBatis's powerful XML mapping capabilities.
- The `src/main/java/.../repository` package contains Spring Data JPA interfaces.
- **`domain` Package Granularity**:
- `domain.entity`: Reserved for classes directly mapped to database tables (POJOs for MyBatis).
- `domain.model`: For more general-purpose domain objects or aggregate roots that do not map one-to-one with individual tables.
- `domain.view`: Specifically for Data Transfer Objects (DTOs) used in read-only scenarios (e.g., query results, report structures).
- `domain.web.request` / `domain.web.response`: Clearly separated DTOs for incoming API requests and outgoing API responses, strictly adhering to the API contract and decoupled from internal domain entities.
- **`manager` and `processor` Packages**: These packages imply a layered architecture where "managers" coordinate operations involving multiple services or repositories, while "processors" may handle specific aspects of business processes. It is mandatory to clearly define the responsibilities of classes in these packages to prevent anti-patterns such as "anaemic domain model" or "god objects".
- **`security` Package**: This sub-package contains custom Spring Security components beyond the initial configuration, such as custom authentication types and providers, indicating a tailored security implementation.
- **`properties` Package**: This package is for custom `@ConfigurationProperties` classes, facilitating type-safe access to application settings defined in YML files — well-positioned.
- **`extension` Package**: This is a flexible area for application-specific extensions, such as custom Jackson serialisers or Redis customisations. It should be used sparingly to avoid becoming a "miscellaneous" dumping ground.
## Security (Spring Security)
- **Mandatory Use**: Spring Security is mandatory in all Spring Boot web applications.
- **Authentication & Authorisation**: Configure authentication mechanisms (e.g., OAuth 2.0, JWT, session-based) and authorisation rules in `SecurityConfig.java`.
- **CSRF Protection**: Ensure CSRF protection is enabled for state-modifying operations, unless there is a strong reason to disable it (e.g., stateless APIs with other mechanisms already in place).
- **CORS**: Correctly configure Cross-Origin Resource Sharing (CORS) according to frontend deployment requirements.
- **Third-Party Identity Providers**: When integrating with third-party identity providers (e.g., Microsoft Entra ID), follow best practices for secure token handling and user provisioning. Sensitive credentials must be securely managed (e.g., environment variables, Vault).
- **Input Validation**: Always validate all user input on the server side to prevent common vulnerabilities such as SQL injection, XSS, etc.
- **Content Security Policy (CSP)**: Consider implementing a robust CSP for the frontend to mitigate XSS attacks.
@@ -0,0 +1,55 @@
---
title: Fix macOS Terminal Host Name Showing IP Segments Under Private DNS
tags:
- macos
- dns
- terminal
author:
name: Zihlu Wang
email: real@zihluwang.me
---
In some enterprise or home private network environments, reverse DNS lookups may resolve a device's private IP to a hostname starting with `192`, `172`, or `10`. When this happens, the macOS terminal prompt changes from the normal `user@MacBook-Pro` to something like `user@192-168-1-100`, which can be distracting.
## Cause
When starting a terminal session, macOS performs a reverse DNS lookup to determine the hostname for the current IP address. If the private DNS server returns a hostname derived from IP octets (e.g. `192-168-1-100.example.com`), the system adopts it as the Host Name and the terminal prompt reflects it.
## Fix
Use the built-in `scutil` (System Configuration Utility) to pin the Host Name to your preferred value.
### Check Current State
```shell
# View the current Host Name (may be empty or overridden by DNS)
scutil --get HostName
# View the local Bonjour name
scutil --get LocalHostName
# View the computer name shown in Finder
scutil --get ComputerName
```
### Set the Host Name
```shell
sudo scutil --set HostName "MacBook-Pro"
```
Prefer a name without spaces or special characters, such as `MacBook-Pro`, `My-Mac`, or your device serial number.
### Verify
Open a new terminal window — the value after `@` in the prompt should now show your chosen hostname.
```shell
scutil --get HostName
# Output: MacBook-Pro
```
## Notes
- `HostName` only affects the network-level hostname. `LocalHostName` (Bonjour) and `ComputerName` (Finder display) are managed independently.
- If the issue returns after a reboot, check `/etc/hosts` for conflicting entries or verify whether the DHCP/DNS server continues to push an undesired hostname.
+200
View File
@@ -0,0 +1,200 @@
---
title: MinIO Administration Guide for New Versions
tags:
- minio
- storage
- s3
- devops
- cheatsheet
author:
name: Zihlu Wang
email: real@zihluwang.me
---
In newer versions, MinIO has removed administrative functionality from the Web UI. You now need to use the **MinIO Client (mc)** command-line tool for all management operations.
## Installing MinIO Client (mc)
### Windows:
```powershell
# Download mc.exe
Invoke-WebRequest -Uri "https://dl.min.io/client/mc/release/windows-amd64/mc.exe" -OutFile "mc.exe"
# Or using Chocolatey
choco install minio-client
```
### Linux/macOS:
```bash
# Linux
wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
sudo mv mc /usr/local/bin/
# macOS
brew install minio/stable/mc
```
## Configuring the MinIO Client
```bash
# Add a MinIO server alias
mc alias set myminio http://localhost:9000 minioadmin minioadmin
# Verify the connection
mc admin info myminio
```
## User Management
### Creating Users
```bash
# Create a new user
mc admin user add myminio newuser newpassword
# List all users
mc admin user list myminio
```
### Creating Access Keys and Secret Keys
```bash
# Create a service account for a user (generates AccessKey/SecretKey)
mc admin user svcacct add myminio newuser
# Or specify custom AccessKey and SecretKey
mc admin user svcacct add myminio newuser --access-key "MYACCESSKEY123" --secret-key "MYSECRETKEY456"
# View a user's service accounts
mc admin user svcacct list myminio newuser
```
## Permission Management
### Creating Policies
```bash
# Create a policy file policy.json
cat > policy.json << EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::mybucket/*"
]
}
]
}
EOF
# Add the policy
mc admin policy add myminio mypolicy policy.json
# Assign the policy to a user
mc admin policy set myminio mypolicy user=newuser
```
## Bucket Management
```bash
# Create a bucket
mc mb myminio/mybucket
# List buckets
mc ls myminio
# Set bucket policy
mc policy set public myminio/mybucket
```
## Common Administration Commands
```bash
# View server information
mc admin info myminio
# View server configuration
mc admin config get myminio
# Restart the server
mc admin service restart myminio
# View logs
mc admin logs myminio
# View statistics
mc admin prometheus metrics myminio
```
## Practical Script Example
Create an administration script `setup-minio.sh`:
```bash
#!/bin/bash
MINIO_ALIAS="myminio"
MINIO_URL="http://localhost:9000"
ADMIN_USER="minioadmin"
ADMIN_PASS="minioadmin"
# Configure the MinIO client
mc alias set $MINIO_ALIAS $MINIO_URL $ADMIN_USER $ADMIN_PASS
# Create an application user
APP_USER="appuser"
APP_PASS="apppassword"
mc admin user add $MINIO_ALIAS $APP_USER $APP_PASS
# Create a service account and retrieve AccessKey/SecretKey
echo "Creating service account for $APP_USER..."
CREDENTIALS=$(mc admin user svcacct add $MINIO_ALIAS $APP_USER --json)
ACCESS_KEY=$(echo $CREDENTIALS | jq -r '.accessKey')
SECRET_KEY=$(echo $CREDENTIALS | jq -r '.secretKey')
echo "Generated credentials:"
echo "Access Key: $ACCESS_KEY"
echo "Secret Key: $SECRET_KEY"
# Create a bucket
mc mb $MINIO_ALIAS/app-bucket
# Set a read-only policy
mc policy set download $MINIO_ALIAS/app-bucket
```
## Web Console Access
Although administrative functionality has been removed, you can still access the MinIO Console via:
```bash
# Launch the MinIO Console (if installed separately)
mc admin console myminio
```
Alternatively, specify the console address when starting the MinIO server:
```bash
minio server /data --console-address ":9001"
```
## Summary
Managing the new MinIO relies entirely on the `mc` command-line tool:
1. **Install the mc client**
2. **Configure the server alias**
3. **Use `mc admin` commands for user, permission, and bucket management**
4. **Generate AccessKeys/SecretKeys via `mc admin user svcacct`**
While this approach requires command-line operations, it provides more powerful and flexible management capabilities, particularly suited for automated deployment and script-based management.
+31
View File
@@ -0,0 +1,31 @@
---
title: MyBatis Flex
tags:
- java
- mybatis
- spring-boot
- orm
- framework
author:
name: Zihlu Wang
email: real@zihluwang.me
---
> This article only aims at using `MyBatis Flex` in Spring Boot 3
## Installation and Configuration
### Installation
Add the following codes to `libs.versions.toml` in `gradle` :
```toml
[versions]
mybatisFlexVersion = "X.Y.Z"
hikariVersion = "X.Y.Z"
[libraries]
hikari = { group = "com.zaxxer", name = "HikariCP", version.ref = "hikariVersion" }
mybatisFlex-processor = { group = "com.mybatis-flex", name = "mybatis-flex-processor", version.ref = "mybatisFlexVersion" }
mybatisFlex-starter = { group = "com.mybatis-flex", name = "mybatis-flex-spring-boot3-starter", version.ref = "mybatisFlexVersion" }
```
@@ -0,0 +1,212 @@
---
title: Accelerating Fuzzy Search in PostgreSQL with Tokenisation
tags:
- postgresql
- zhparser
- full-text-search
- performance
author:
name: Siu Jam Oh
email: jamo.siu@gmail.com
---
## Background and Challenges
As our business data surpassed **2 million rows**, traditional `LIKE '%keyword%'` fuzzy queries triggered frequent database I/O alerts, with query response times degrading from milliseconds to seconds. To improve search efficiency and support Chinese semantics, we decided to introduce the `zhparser` extension for full-text search.
## Evolution Path and Environment Adaptation
This implementation went through four key phases, each addressing distinct technical challenges:
### CentOS 7.9 VM (Feasibility Validation)
- **Goal**: Validate the compatibility of `SCWS` + `zhparser` on older systems.
- **Core Action**: Manually compiled `postgresql-16.2` from source in a CentOS 7.9 environment, got the extension working end-to-end.
**Conclusion**: Confirmed significant performance improvements from the tokenisation approach for Chinese search.
### Local Docker Container (Containerisation Exploration)
- **Goal**: Initial testing in a complete local system.
- **Core Action**: Injected binary `.so` files via `docker cp`, resolved `ldconfig` dynamic library path visibility issues.
- **Discovery**: Identified that **missing dictionary files** cause tokenisation to degrade into single-character (particle-level) tokenisation — a critical failure point.
### EulerOS 2.0 Test Server (Self-Compiled Environment Adaptation)
- **Goal**: Adapt to the production OS architecture (x86_64) and self-compiled PostgreSQL installation.
- **Core Issue**: Resolved `libscws.so.1` loading errors.
- **Key Solutions**:
- Ensured the `postgres` runtime user has access permissions to `/usr/local/scws/lib`.
- Modified `systemd` service environment variables or created `/usr/lib64` symlinks to force refresh library search paths.
### Production Deployment Preparation (Final Tuning)
- **Goal**: Ensure query stability at 2M+ data volume.
- **Optimisation**: Addressed cases where non-semantic fragments (e.g., "古唐合") returned no results by establishing a "full-text search first + `pg_trgm` index assist" degraded query strategy.
## Core Installation and Configuration Steps (Self-Compiled Environments)
### Installing the `SCWS` Tokenisation Engine
SCWS is the underlying core dependency of `zhparser` and must be installed first.
1. **Download and Extract**: Download the source package (e.g., `scws-1.2.3`).
2. **Compile and Install**:
```bash
./configure --prefix=/usr/local/scws
make && make install
```
3. **Verify the Library**: Ensure `/usr/local/scws/lib/libscws.so.1` exists.
### Compiling and Installing `zhparser`
This step requires `pg_config` from the self-compiled PostgreSQL installation.
1. **Get the Source**: Clone the `zhparser` project from GitHub.
2. **Compile with Specified Path**:
```bash
# Ensure pg_config is in PATH, or specify manually
make USE_PGXS=1 PG_CONFIG=/usr/local/pgsql/bin/pg_config
make USE_PGXS=1 PG_CONFIG=/usr/local/pgsql/bin/pg_config install
```
*Note: The `install` step automatically places `zhparser.so` into PG's `pkglibdir` and extension scripts into the `extension` directory.*
### Resolving Dynamic Library Dependencies
1. **Refresh System Cache**:
```bash
echo "/usr/local/scws/lib" > /etc/ld.so.conf.d/scws.conf
ldconfig
```
2. **Permission Check**: Ensure the OS user running `postgres` has `rx` permission on `/usr/local/scws/lib`.
3. **Force Symlink (Alternative)**: If `ldconfig` fails, symlink the library file to `/usr/lib64`.
### Restarting the Database
After modifying system shared library configuration, the PostgreSQL process must be restarted to reload environment variables and linked libraries.
```bash
## Restart using pg_ctl (paths may vary for self-compiled installations)
/usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data restart
## Or restart via systemd (if registered as a service)
systemctl restart postgresql
```
### Database-Level Initialisation
Connect to `psql` and run the logical configuration:
```sql
-- Create the extension
CREATE EXTENSION zhparser;
-- Create a full-text search configuration and bind the tokeniser
CREATE TEXT SEARCH CONFIGURATION chinese (PARSER = zhparser);
-- Add token mappings
ALTER TEXT SEARCH CONFIGURATION chinese ADD MAPPING FOR n,v,a,i,e,l,t,b WITH simple;
-- [Optional] Specify the dictionary path for self-compiled installations
-- ALTER DATABASE postgres SET zhparser.dict_path = '/usr/local/scws/etc/dict.utf8.xdb';
```
## Performance Analysis and Pitfalls
### Index Performance Bottleneck Analysis
During testing, it was found that even exact queries suffered from `Bitmap Index Scan` due to an improperly designed composite index (with `create_time` as the leading column), resulting in query times as high as **482 ms**.
- **Improvement**: Created single-column **B-tree** indexes on frequently searched columns, reducing response time to under **10 ms** with `Index Scan`.
### GIN Index and Non-Semantic Matching
- **Cross-Word Truncation**: The tokenisation engine is semantic-based, so truncated strings like "古唐合" may fail to match with `@@` due to tokenisation boundaries.
- **Mitigation Strategy**: Adopt a "waterfall search" approach. Full-text search (FTS) first; if the result set is empty, automatically degrade to `LIKE` fuzzy queries, assisted by `pg_trgm` indexing.
## Final Deployment Strategy: Dual-Track Parallel Retrieval
After analysing the data, we found that approximately 1.24% of account names contain non-standard Simplified Chinese characters, and some company names in the database are unusual enough to cause search failures. We adopted a "stepwise degradation" strategy:
- **Step 1: Full-Text Search (Fast Track)**: Use GIN index for `@@` matching.
- **Step 2: Result Evaluation**: If the result set is empty, check whether the search term contains letters or suspected Traditional Chinese characters.
- **Step 3: Fuzzy Fallback (Safe Fallback)**: Execute `LIKE '%keyword%'`. Although slower, since this serves as a "gap-fill" logic triggered only ~1% of the time, it won't impose overall system pressure.
## Search Optimisation: Integrating a Custom Business Lexicon
To address issues like company brand names being incorrectly segmented by full-text search (e.g., "元一" being split into a numeral and a quantifier), we built an automated maintenance pipeline from data extraction to index rebuild.
### Lexicon Extraction and Preprocessing
Leverage the structural parsing capabilities of **`companynameparser`** to strip region names and industry suffixes, and use **`jieba`** for semantic validation to ensure core brand name integrity:
- **Extraction Logic**: Traverse all `buyer_unique_name` values via a Python script, extracting the core `brand` field.
- **Weight Compensation**: For words prone to fragmentation (e.g., those containing "元", "一", "三"), manually boost TF (term frequency weight) to **50.060.0** to ensure their priority overrides built-in quantifier rules.
- **Output Specification**: Produce SCWS-compliant 4-field `UTF-8` text (WORD, TF, IDF, ATTR). Use tab `\t` separators to avoid parsing anomalies.
### Lexicon Compilation and Deployment
:::tip
Users compiling `xdb` binary dictionary files on Windows can visit OnixBytes [GitHub](https://github.com/onixbyte/scws/releases/tag/1.2.3) or [GitLab](https://git.onixbyte.cn/onixbyte/scws/-/releases/1.2.3) pages to download the native scws command-line tool for Windows, pre-compiled using MingW.
:::
Convert the text dictionary to SCWS's efficient binary format (XDB):
1. **Compile the Binary Dictionary**:
```bash
# Use scws-gen-dict to generate an encrypted binary lexicon
/usr/local/scws/bin/scws-gen-dict -i custom_company.txt -o /usr/local/scws/etc/custom_company.xdb -c utf8
```
2. **File Distribution and Permissions**: Move the generated `.xdb` file to the tokenisation data directory and ensure the `postgres` user has read permission:
```bash
cp custom_company.xdb /usr/local/pgsql/share/tsearch_data/
chown postgres:postgres /usr/local/pgsql/share/tsearch_data/custom_company.xdb
```
### Database Parameter Configuration
Modify `postgresql.conf` to force-load `zhparser` and its custom extension lexicon:
```plain text
## Preload the extension library (requires restart to take effect)
shared_preload_libraries = 'zhparser'
## Load custom external dictionaries (use paths relative to tsearch_data)
zhparser.extra_dicts = 'custom_company.xdb'
```
### Hot Index Rebuild and Verification
Since tokenisation rules have changed, existing data must be semantically synchronised via index rebuild:
**Physically Restart the Service**:
```bash
su - postgres -c "/usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data restart"
```
**Online Index Rebuild**: Use the `CONCURRENTLY` keyword to refresh the GIN index without blocking DML operations on 400K rows:
```bash
REINDEX INDEX CONCURRENTLY index_name;
```
**Tokenisation Effectiveness Verification**:
```sql
-- Expected part-of-speech should show as n (noun), not x (unknown)
SELECT * FROM ts_debug('chinese', '元一能源');
```
**Optimisation Notes:**
- **Explicit Weight Compensation**: This is the key technique that resolved the "元一" tokenisation failure (shown as `x`).
- **Distinguish Restart from Reload**: `shared_preload_libraries` must be activated via `restart`, not a simple reload.
+22
View File
@@ -0,0 +1,22 @@
---
title: The Quartile Method
tags:
- statistics
- algorithm
- data-analysis
- math
author:
name: Zihlu Wang
email: real@zihluwang.me
---
The quartile method is a commonly used statistical technique primarily employed for data analysis and presentation. The method divides a data set into four equal parts, each containing one quarter of the data. The key statistics include the first quartile (Q1), second quartile (Q2, the median), and third quartile (Q3).
- The first quartile (`Q1`), also known as the lower quartile, is the value at the 25th percentile of a data set sorted in ascending order.
- The second quartile (`Q2`), also known as the median, is the value at the 50th percentile of a data set sorted in ascending order.
- The third quartile (`Q3`), also known as the upper quartile, is the value at the 75th percentile of a data set sorted in ascending order.
- The interquartile range (`IQR`) is the difference between the third quartile and the first quartile, used to measure the dispersion of the middle 50% of data. The formula is IQR = Q3 - Q1. The IQR is commonly used in constructing box plots, an effective way to describe data distribution, particularly useful for identifying outliers.
Upper bound = Q3 + 1.5 × IQR.
Lower bound = Q1 - 1.5 × IQR.
+140
View File
@@ -0,0 +1,140 @@
---
title: Setting Up an LDAP Service
tags:
- ldap
- openldap
- authentication
- linux
- devops
author:
name: Zihlu Wang
email: real@zihluwang.me
---
Setting up an LDAP (Lightweight Directory Access Protocol) server is a core step in implementing enterprise-level **centralised identity authentication** and **permission management**. The most commonly used open-source implementation is **OpenLDAP**.
Below are detailed steps for setting up an OpenLDAP server on Debian/Ubuntu-based Linux systems.
## Environment Preparation and Installation
Before starting, ensure your system packages are up to date and the hostname is properly configured.
```bash
## Update the system
sudo apt update && sudo apt upgrade -y
## Install OpenLDAP and management tools
## slapd is the daemon, ldap-utils is the command-line client tool
sudo apt install slapd ldap-utils -y
```
> During installation, you will be prompted to set the **LDAP administrator password**. Be sure to remember this password — it will be used frequently during configuration.
## Configuring the OpenLDAP Server
Although the installation performs some initial setup, we typically need to customise the configuration for a specific domain (e.g., `example.com`).
### Reconfigure slapd
Run the following command to enter the interactive configuration interface:
```bash
sudo dpkg-reconfigure slapd
```
**Configuration recommendations**:
1. **Omit OpenLDAP server configuration?** Choose **No**.
2. **DNS domain name:** Enter your domain (e.g., `centre.example.com`). This determines your Base DN, such as `dc=centre,dc=example,dc=com`.
3. **Organization name:** Enter your organisation name.
4. **Administrator password:** Enter the administrator password you set earlier.
5. **Database backend:** **MDB** is recommended.
6. **Remove database when slapd is purged?** Choose **No**.
7. **Move old database?** Choose **Yes**.
## Understanding the LDAP Hierarchy
LDAP data is stored in a **tree structure**. To facilitate management, we typically create two "Organisational Units" (OUs): one for users (People) and one for groups (Groups).
## Creating Organisational Units (OUs)
In LDAP, we use **LDIF** (LDAP Data Interchange Format) files to add or modify directory entries.
Create a file named `base.ldif`:
```text
## Create the users organisational unit
dn: ou=people,dc=centre,dc=example,dc=com
objectClass: organizationalUnit
ou: people
## Create the groups organisational unit
dn: ou=groups,dc=centre,dc=example,dc=com
objectClass: organizationalUnit
ou: groups
```
**Import the data:**
```bash
## Import the LDIF file into the database as the administrator
ldapadd -x -D "cn=admin,dc=centre,dc=example,dc=com" -W -f base.ldif
```
## Adding Users and Groups
Create a file named `groups.ldif` to define a new group:
```text
## Define a new group
dn: cn=developers,ou=groups,dc=centre,dc=example,dc=com
objectClass: inetOrgGroup
cn: developers
```
Create a file named `users.ldif` to define a new user:
```text
## Define a new user
dn: uid=jbloggs,ou=people,dc=centre,dc=example,dc=com # The Distinguished Name (DN) of this entry.
objectClass: inetOrgPerson # Object class — inetOrgPerson represents an internet organisation person. It enables common contact attributes such as email, displayName, telephoneNumber, etc.
objectClass: posixAccount # Makes this entry compatible with Unix/Linux system accounts. With it, the user can log into Linux servers and has a UID, GID, and home directory.
objectClass: shadowAccount # Used to manage password ageing (expiry, change warnings, etc.), corresponding to /etc/shadow functionality in Linux.
uid: jbloggs # The user's login name (User ID). This is typically what you enter on the Linux login screen.
sn: Bloggs # Surname. Required by the inetOrgPerson class.
givenName: Joe # First name.
cn: Joe Bloggs # Common Name. The standard display name for an LDAP entry.
displayName: Joe Bloggs # The friendly name displayed in graphical interfaces or email clients.
uidNumber: 10000 # The user's numeric ID in the Linux system.
gidNumber: 5000 # The numeric ID of the user's primary group.
userPassword: {SSHA}password-hash-or-plaintext # The user's encrypted password.
homeDirectory: /home/jbloggs # The path to the user's home directory after logging into Linux.
loginShell: /bin/bash # The shell environment the user gets after logging in.
```
> If you plan to integrate **GitLab**, **Jenkins**, or a **VPN** later, they typically search the `uid` attribute to verify login names.
### FAQ
#### If the user doesn't need server login permissions, can the `objectClass: posixAccount` be removed?
**Yes, it can be completely removed**, but you need to be aware of the "binding relationships" between attributes.
If you only need this user for web application logins (e.g., GitLab, Jenkins, Wiki) or as an email contact, and they don't need to log into a Linux server via SSH or console, removing `posixAccount` is the more standard approach.
When you remove `objectClass: posixAccount`, the following attributes **must also be deleted**, as they belong to that class's mandatory or optional attributes:
- `uidNumber`
- `gidNumber`
- `homeDirectory`
- `loginShell`
Additionally, `shadowAccount` is also typically associated with system logins — if you don't need to manage Linux password expiry policies, it can also be removed.
## Follow-up Suggestions and Management Tools
Command-line LDAP management can be cumbersome. The following tools are recommended for visual administration:
- **phpLDAPAdmin**: A web-based management interface, ideal for quick onboarding.
- **Apache Directory Studio**: A powerful cross-platform desktop client, suitable for complex architecture design.
- **Security Hardening**: By default, LDAP transmits data in plain text. It is recommended to configure **LDAPS (LDAP over SSL/TLS)** to encrypt communication on port 636.
+89
View File
@@ -0,0 +1,89 @@
---
title: Using JSON in MySQL
tags:
- mysql
- json
- database
author:
name: Zihlu Wang
email: real@zihluwang.me
---
MySQL (since version 5.7) **does not directly support a data type called `jsonb`**. `jsonb` is a data type specific to PostgreSQL, which stores JSON data in a binary format with pre-parsing for faster access and manipulation during queries.
However, MySQL's `JSON` data type is functionally and internally similar to PostgreSQL's `jsonb` in many respects, particularly when it comes to querying data.
The `JSON` data type in MySQL was introduced in MySQL 5.7 and has the following characteristics:
1. **Binary Storage**: Like PostgreSQL's `jsonb`, MySQL's `JSON` type data is stored in an **internal binary format** rather than as a plain text string. This makes reading and manipulating JSON data more efficient, as the database does not need to parse text-format JSON strings on every query.
2. **Automatic Validation**: When you insert or update a `JSON` column, MySQL automatically validates that its content is a valid JSON document. If not, it throws an error.
3. **Optimised Storage**: The binary format is also space-optimised, typically more compact than storing JSON in raw text format.
MySQL provides a powerful set of functions and operators for querying and manipulating `JSON` data, very similar to what you'd expect from `jsonb`:
1. **`->` (JSON Extract Operator)**: Extracts a value from a JSON document. It returns a JSON value.
```sql
SELECT my_json_column->'$.key' FROM my_table;
-- Example: extract the name property of a user object
-- Assuming my_json_column stores {'user': {'name': 'Alice'}}
SELECT json_data->'$.user.name' FROM my_table;
```
2. **`->>` (JSON Unquote Operator)**: Extracts a value from a JSON document and **automatically unquotes it**, typically returning a scalar value (e.g., string, number). This is equivalent to `JSON_UNQUOTE(JSON_EXTRACT(...))`.
```sql
SELECT my_json_column->>'$.key' FROM my_table;
-- Example: extract the name property of a user object (returns the string 'Alice' directly)
SELECT json_data->>'$.user.name' FROM my_table;
```
3. **`JSON_EXTRACT(json_doc, path, ...)`**: Explicitly extracts data from a JSON document.
```sql
SELECT JSON_EXTRACT(my_json_column, '$.key') FROM my_table;
```
4. **`JSON_CONTAINS(json_doc, candidate, path)`**: Checks whether a JSON document contains a specified value.
```sql
-- Check whether the tags array contains 'backend'
-- Assuming my_json_column stores {'tags': ['frontend', 'backend']}
SELECT * FROM my_table WHERE JSON_CONTAINS(json_data->'$.tags', '"backend"');
```
5. **`JSON_SEARCH(json_doc, one_or_all, search_str, escape_char, path, ...)`**: Returns the path to a specified string within a JSON document.
```sql
-- Find the path to a value of 'test'
SELECT JSON_SEARCH(my_json_column, 'one', 'test') FROM my_table;
```
6. **`JSON_TABLE(json_doc, path COLUMNS ... )` (MySQL 8.0 and later)**: A very powerful function that "expands" JSON data into relational rows and columns, ideal for complex queries and reporting.
```sql
-- Assuming json_data stores {'items': [{'id': 1, 'name': 'A'}, {'id': 2, 'name': 'B'}]}
SELECT *
FROM my_table,
JSON_TABLE(json_data, '$.items[*]' COLUMNS(
itemId INT PATH '$.id',
itemName VARCHAR(50) PATH '$.name'
)) AS jt;
```
Like PostgreSQL's `jsonb`, efficient querying on JSON fields typically requires indexing. Since the content of JSON fields is dynamic, MySQL does not directly support creating traditional B-tree indexes on a specific internal path of a JSON field. However, you can achieve this through **Virtual Generated Columns**:
1. **Create a Virtual Column**: Define a virtual column whose value is extracted from a specific path in the JSON field.
```sql
ALTER TABLE my_table
ADD COLUMN user_name VARCHAR(255) AS (json_data->>'$.user.name') VIRTUAL;
```
2. **Create an Index on the Virtual Column**: This way, when you query `WHERE json_data->>'$.user.name' = 'Alice'`, the MySQL optimiser can use the `idx_user_name` index, significantly improving query performance.
```sql
CREATE INDEX idx_user_name ON my_table (user_name);
```
Although MySQL does not have the exact name `jsonb`, its `JSON` data type provides highly similar functionality: binary storage optimisation, automatic validation, and rich query operators and functions. By combining virtual columns with indexes, MySQL can deliver query performance and flexibility comparable to PostgreSQL's `jsonb` when working with JSON data.
+85
View File
@@ -0,0 +1,85 @@
---
title: Use Gravatar in First-party Systems
tags:
- gravatar
- avatar
author:
name: Zihlu Wang
email: real@zihluwang.me
---
[Gravatar](https://gravatar.com) (Globally Recognised Avatar) is a service that associates avatar images with email
addresses. When a user registers on your platform with their email, you can display their Gravatar as a default avatar
without building your own image upload and storage pipeline.
## How It Works
Gravatar exposes a simple HTTP endpoint. You compute the SHA256 hash of the user's **lowercased and trimmed** email
address, then embed it in an image URL:
```
https://gravatar.com/avatar/<hash>
```
## Generating the Hash
### Node.js
```js
import { createHash } from "node:crypto";
const email = "user@example.com".trim().toLowerCase();
const hash = createHash("sha256").update(email).digest("hex");
const url = `https://gravatar.com/avatar/${hash}`;
```
### Python
```python
import hashlib
email = "user@example.com".strip().lower()
hash = hashlib.sha256(email.encode()).hexdigest()
url = f"https://gravatar.com/avatar/{hash}"
```
### Shell
```shell
echo -n "user@example.com" | tr '[:upper:]' '[:lower:]' | sha256sum | cut -d ' ' -f1
```
## URL Parameters
The `/avatar/` endpoint accepts several query parameters to customise the result:
| Parameter | Description | Example |
|-----------|-----------------------------------------|----------------|
| `s` | Size in pixels (default: 80) | `?s=200` |
| `d` | Default image when no Gravatar is found | `?d=identicon` |
| `r` | Content rating (`g`, `pg`, `r`, `x`) | `?r=g` |
### Default Image Options (`d`)
- `identicon` — a geometric pattern based on the hash
- `robohash` — a generated robot image
- `retro` — an 8-bit style pixelated face
- `monsterid` — a generated monster cartoon
- `wavatar` — a generated face
- `mp` — a generic silhouette (Mystery Person)
- `blank` — a transparent PNG
- A custom URL (must be URL-encoded)
### Putting It Together
```html
<img
src="https://gravatar.com/avatar/a3b4c5d6e7f8?s=160&d=robohash&r=g"
alt="User avatar"
width="160"
height="160"
/>
```
Always pass the `d` parameter to avoid broken images for users who have not set up a Gravatar. `identicon` and
`robohash` are popular choices because they generate a unique, recognisable image for every hash.
@@ -0,0 +1,38 @@
---
title: Version Control and Code Review
tags:
- git
- code-review
- best-practice
- workflow
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## GitFlow Workflow
Version control will use the GitFlow branching model, consisting of `main`, `develop`, `feature`, `release`, and
`hotfix` branches.
- `main`: Production-ready code. Only `release` and `hotfix` branches are merged into `main`.
- `develop`: Integration branch for upcoming features.
- `feature/*`: Branches for new features, branched off `develop`.
- `release/*`: Branches for preparing new production releases, branched off `develop`.
- `hotfix/*`: Branches for urgent production bug fixes, branched off `main`.
## Pull Requests/Merge Requests
All code changes (except direct pushes to feature branches) must be submitted via pull requests.
## Code Review
- Each pull request must be reviewed by at least one other developer.
- Reviewers are responsible for checking compliance with these coding standards, code quality, logical correctness, and
test coverage.
- IntelliJ IDEA's integrated code analysis tools should be run locally before creating a PR.
## Commit Messages
Write clear, concise, and descriptive commit messages that explain what was changed and why. If possible, follow the
Conventional Commits format (e.g., `feat: add user registration endpoint`).