Skip to main content

2025-12-23

A Word

Rippling with the waves for thousands of miles, where is the spring river without moonlight! --- “Spring River, Flower, Moon, Night” · Tang Dynasty · Zhang Ruoxu

Details

Full Text The spring river tides rise to meet the sea, the sea and the bright moon rise with the tide.
Rippling with the waves for thousands of miles, where is the spring river without moonlight!
The river winds around fragrant islets, moonlight on flower forests looks like falling frost.
In the empty air, flowing frost seems unseen, on the shoals, white sand cannot be discerned.
River and sky merge in one pure hue, a solitary bright moon hangs in the vast sky.
Who by the river first beheld the moon, in what year did the moon first shine upon mankind?
Life passes on generation after generation, yet the river moon looks the same year after year.
I wonder whom the river moon awaits, only seeing the long river sending flowing waters away.
A single cloud drifts leisurely, endless sorrow at Green Maple Islet.
Who tonight is the boatman adrift, where is the longing at the moonlit tower?
Pitifully the moon lingers over the tower, shining on the mirror stand of the departed.
Through jade doors and curtains it cannot be rolled away, from washing stones it brushes past again.
At this moment we gaze at each other but cannot hear, I wish to follow the moonlight and shine upon you.
Wild geese fly far, yet their light does not pass, fish and dragons leap, water forming patterns.
Last night I dreamed of falling flowers by a quiet pool, pitiful that spring is half gone and I have not returned home.
The river carries spring away, soon to be gone, the moon sinks westward over the river pool.
The slanting moon sinks deep into sea mist, endless roads toward Jieshi and Xiaoxiang.
Who knows how many return by moonlight, the setting moon stirs emotion across the river trees.


Docker Registry Study Notes

Basic Concepts of Docker Registry

Docker Registry is a server-side component used to store and distribute Docker images. The official implementation provided by Docker is registry:2 (Distribution). Its core positioning is very clear:

It is only responsible for storing and serving images, not for complex management and governance.

Registry itself is a stateless service; the actual data is stored in the backend storage (local filesystem, object storage, etc.).


The Capability Boundaries of Docker Registry

Before learning and using Registry, you must first clarify what it can / cannot do.

What it can do

  • Private image repository storage

  • Support push / pull / delete

  • Support Basic Auth and TLS

  • Support single-upstream pull-through cache (proxy cache)

  • Support filesystem or object storage as backend

What it cannot do

  • Multiple upstream Registry aggregation

  • Fine-grained RBAC permission model

  • Graphical management interface

  • Image lifecycle management

  • Intelligent routing and policy control

In one sentence:

registry:2 is an “image HTTP storage service,” not an “image management platform.”


Two Typical Usage Modes of Registry

Hosted (Private Repository Mode)

  • Images are docker push’ed by internal systems

  • Registry only stores them

  • Address form:

    registry.example.com:5000/project/app:1.0.0

Suitable for:

  • Internal development images

  • CI/CD build artifacts

  • Offline or semi-offline environments


Pull-Through Cache (Proxy Cache Mode)

Registry can be configured with an upstream (such as Docker Hub):

  1. The image does not exist locally

  2. Forward the request to the upstream and pull it

  3. Cache it locally

  4. Return it to the client

Main purposes:

  • Docker Hub acceleration

  • Networks with instability or rate limiting


How Pull-Through Cache Works

Request Path

docker pull nginx

Docker daemon

registry (proxy mode)

Docker Hub
  • First pull: hits the upstream and caches

  • Subsequent pulls: hit the local cache directly


Proxy Configuration Example (Single Upstream)

version: 0.1
http:
addr: :5000

storage:
filesystem:
rootdirectory: /var/lib/registry

proxy:
remoteurl: https://registry-1.docker.io

Key points:

  • remoteurl can only be configured with one

  • The correct entry for Docker Hub is registry-1.docker.io

  • registry.docker.io is not the Registry V2 API entry


Why Registry Does Not Support Multiple remoteurl Values

This is a design-level limitation, not a configuration capability issue.

Reasons include:

  • Registry V2 token / scope is strongly bound to a single upstream

  • Image namespaces cannot be routed and determined

  • Authentication flows cannot be combined

Conclusion:

The proxy model of registry:2 is “single-upstream caching,” not “multi-source aggregation.”


The Relationship Between Docker Client and Registry

The Role of Registry Mirror

The Docker client can configure image mirrors:

{
"registry-mirrors": ["http://registry.example.com:5000"]
}

Behavior description:

  • Prefer pulling Docker Hub images through the mirror

  • If the mirror does not have it, fall back to pulling directly from Docker Hub

Notes:

  • mirror ≠ multi-registry routing

  • Only applies to the Docker Hub namespace


The Security Model of HTTP and HTTPS

HTTP Registry (Insecure)

Characteristics:

  • Easy to deploy

  • Only suitable for intranet environments

The client must declare it explicitly:

{
"insecure-registries": ["registry.example.com:5000"]
}

Characteristics:

  • Secure by default

  • No need for insecure configuration

The client needs to trust the CA:

/etc/docker/certs.d/registry.example.com:5000/ca.crt

Registry Authentication Mechanisms

Basic Auth

  • Based on htpasswd

  • Registry does not maintain a user system

  • Suitable for small-scale usage

Token / OAuth

  • Not supported natively by Registry

  • Usually provided by reverse proxies or upper-layer platforms


Registry Data Storage Model

Stored Content

  • Layer blobs (disk usage)

  • Manifest / index (metadata)

Important Understanding

  • The same layer is stored only once (content-addressable)

  • Deleting a tag does not mean the disk space is freed immediately

  • You must run GC to reclaim disk space


Operational Concerns for Registry

Disk Space

  • Images grow fast

  • Disk usage must be monitored

Backup Strategy

  • Back up the storage directory directly

  • Registry itself is stateless

Garbage Collection (GC)

  • Service must be stopped during GC

  • Not suitable for high-concurrency online environments


Suitable and Unsuitable Scenarios for Registry

Suitable Scenarios

  • Intranet image caching

  • Docker Hub acceleration

  • CI/CD private repository

  • Offline image distribution

Unsuitable Scenarios

  • Enterprise-level multi-team governance

  • Multi-registry aggregation

  • Complex permissions and auditing

  • Visual management requirements


The Role of Registry in the Overall Architecture

An engineering conclusion:

Docker Registry is a “lower-level storage component,” not a “platform-level product.”

In a mature architecture, it is often:

  • Taken over by an upper-layer platform

  • Or exists only as a dedicated cache node


Learning Summary

  • Registry supports only single-upstream proxy

  • remoteurl cannot be configured with multiple values by design

  • registry-1.docker.io is the real entry for Docker Hub

  • Registry Mirror ≠ multi-registry routing

  • A unified entry for multiple upstreams requires a higher-level repository management system

With these understandings established, all Registry behaviors become predictable, explainable, and maintainable.

Systematic Study Notes on Validation, Unique Keys, and String Handling in Spring Boot / Java Projects

This article is a complete engineering-grade summary of our entire discussion. The goal is not merely “to write code,” but to know why it is written this way, what cannot be omitted, and what should not be over-engineered. The content is based on Spring Boot + Java 11/21 + jOOQ, but the principles are broadly applicable.


I. Bean Validation Basics: The Proper Positioning of @Validated

1.1 What @Validated Is

  • @Validated is a Spring-provided annotation to trigger validation

  • It is based on Bean Validation (JSR-303 / JSR-380)

  • The default implementation is Hibernate Validator

Core purpose:

Trigger validation + support validation groups + support method-level validation


1.2 Differences Between @Validated and @Valid

Dimension@Valid@Validated
OriginJSR standardSpring
Group validation
Method parameter validation
Controller parameter validation

Engineering recommendation: In Spring Boot projects, use @Validated consistently.


II. Cross-Field Validation: Why You Must Use “Class-Level Validation”

2.1 The Nature of the Problem

Requirement example:

String a and String b cannot both be empty (null / blank), but it is allowed to fill only one

This is a cross-field combination rule, not a single-field rule.


2.2 Why Field-Level Annotations Are Not Suitable

  • @NotNull / @NotBlank expresses AND

  • The actual business semantics is OR

  • Field-level annotations cannot see “the other field”

Conclusion:

If a rule depends on multiple fields, you must use class-level validation


III. Correct Semantics of String Validation: nullblank

3.1 Distinguishing at the Business Semantics Level

CaseMeaning
nullnot provided
"" / " "provided but meaningless
" 10.1.1.1 "has value but with noise
"1.1 .1.1"invalid input

Business validation cares about “whether it is meaningful,” not just the memory state.


3.2 Java Native vs Apache Commons

Compare two approaches:

s != null && !s.isBlank()
StringUtils.isNotBlank(s)

Conclusion:

  • Java 11+: prefer native isBlank()

  • Do not introduce commons-lang3 just for isNotBlank

  • Dependency governance > saving a few keystrokes


4.1 Standard Implementation

private static String normalize(String s) {
if (s == null) return null;
var t = s.trim();
return t.isBlank() ? null : t;
}

4.2 Why trim() Then isBlank()

  • isBlank() can detect all-whitespace

  • The real purpose of trim() is not to check emptiness, but to remove leading/trailing noise

  • "10.1.1.1" and " 10.1.1.1 " should be treated as the same value in business terms


4.3 Why Not Handle “Inner Spaces”

For example:

1.1 .1.1
1.1. 1.1

Conclusion:

normalize is responsible for normalization, not correction

Reasons:

  • Inner spaces are invalid input

  • Auto-fixing would “silently swallow errors”

  • It should be rejected clearly at the DTO / validation layer

Engineering principle:

Leading/trailing whitespace is noise and can be cleaned; inner whitespace is an error and must be rejected.


V. Everyday Code Style: Getter Calls vs Local Variables

5.1 Two Styles Compared

if (a.getS() != null && !a.getS().isBlank())
var s = a.getS();
if (s != null && !s.isBlank())

Prefer assigning to a local variable before checking

Reasons:

  • Avoid repeated expressions

  • Reduce reading and debugging cost

  • Easier to extend logic

  • No substantive performance difference


VI. Modeling the Core Problem of Unique Key Validation

6.1 The True Semantics of a Unique Key

Unique key validation is not asking:

“Does this set of fields exist?”

It is asking:

“Is there another record that has the same unique-key semantics as mine?”

“Another record” is the key phrase.


6.2 When a Unique Key Contains Branching Logic

Example:

  • systemName + sid + client + scheme + ip

  • Or systemName + sid + client + scheme + domain

Implicit rules:

  • ip and domain are mutually exclusive

  • ip has higher priority

Such rules should be:

  • Validated at the DTO layer (at least one is non-blank)

  • Reflected consistently in DAO-layer condition construction


VII. Differences in Uniqueness Validation Between Create and Update Scenarios

7.1 Fundamental Difference

ScenarioDoes current record exist?Need to exclude itself?
CreateNo
UpdateYes

7.2 Why Update Must Exclude Itself

If you do not exclude:

exists (where unique key condition)
  • When updating without changing the unique key

  • The query will always hit itself

  • It will be falsely judged as a conflict

Correct semantics:

exists (
where unique key condition
and id <> currentId
)

7.3 Why Create Cannot Use id != null

  • In SQL, id <> null evaluates to unknown

  • It can cause the condition to fail or lead to incorrect logic

  • jOOQ should not generate such conditions either


8.1 Method Signature

public boolean existsConflictUniqueKey(
@Nullable Long currentId,
String systemName, String sid, Short client,
@Nullable String ip, @Nullable String domain,
Scheme scheme
)

8.2 Core Logic

var cond = baseKeyCond(...);
if (cond == null) return false;

if (currentId != null) {
cond = cond.and(T.ID.ne(currentId)); // Update
}

return fetchExists(cond);

Calling convention:

  • Create: currentId = null

  • Update: currentId = id


IX. The Outer “Whether the Unique Key Changed” Check: Purpose and Boundaries

9.1 What the Outer Comparison Can Do

if (oldKey.equals(newKey)) {
skip exists query
}

Benefits:

  • Reduce database queries

  • Improve performance and user experience


9.2 What the Outer Comparison Cannot Do

It cannot guarantee uniqueness, for reasons including:

  • Concurrent inserts / concurrent updates

  • Data drift (stale read)

  • TOCTOU (time-of-check to time-of-use gap)

Conclusion:

Outer comparison is an optimization, not a correctness guarantee


10.1 Three-Layer Defense Model

First layer: DTO / parameter validation
  • Non-blank

  • At least one field exists

  • Valid format (e.g., IP)

Second layer: application-layer uniqueness check
  • Create: exists(key)

  • Update: exists(key) AND id != currentId

  • Outer comparison used to reduce queries

Third layer: database unique constraint (final safeguard)
  • Prevent concurrency bypass

  • Catch unique constraint exception → convert to business exception


XI. Naming Principles for Validation Annotations and Validators

For example:

@AOrBNotBlank

Problems:

  • Breaks when field names change

  • Annotation count explodes

  • Poor reusability


@AtLeastOneNotBlank(fields = {"a", "b"})

Principle:

Annotation name = rule semantics field names = parameters validator = rule engine


XII. Key Engineering Conclusions Summary

  1. @Validated is the preferred validation annotation in Spring projects

  2. Cross-field rules must use class-level validation

  3. String validation focuses on semantics, not just null

  4. normalize is for normalization, not correction

  5. Local variables are preferable to repeated getter calls

  6. Update uniqueness checks must exclude itself

  7. Create scenarios must not generate id != null

  8. Outer key comparison is optimization, not correctness guarantee

  9. Database unique constraints are the final safeguard

  10. Correctness > performance optimization > code brevity


XIII. One Engineering Summary Sentence

Validation is a semantics problem, uniqueness is a concurrency problem, the database is the final judge.

Once you separate these three clearly, your code is already standing on the side of “long-term maintainability.”