Files
greptimedb/docs
+10 602fdd3a27 chore: prepare v1.3.1 release candidate (#10264)
* fix: bound metric snapshot memory and reduce export overhead (#9505)

* fix(mito): bound retained scan memory

Compact unused tag dictionary values before merge retention and avoid splitting
a single scan source into redundant batches. Include regression coverage.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* perf(snapshot): bound writes and reduce logical export overhead

Bound metadata writes to 8 MiB and abort failed writers. Preserve compatible
string dictionaries, bound conversion work, combine Parquet row groups and
filter single-table exports before routing. Include resource-boundary and
packed-restore regression coverage.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* docs: clarify metric snapshot acceptance criteria

Specify populated scale fixtures, controlled S3 request delay, native macOS
validation, paging exclusions and alternating comparison rounds.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(mito): split batches only for merged readers

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* test(snapshot): cover supported types in both layouts

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* docs: consolidate metric snapshot experiment conclusions

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(mito): honor series statistics when splitting L0 scans

Apply the existing series-density heuristic to L0 files with known statistics and include them in the batch-size estimate. Preserve the fallback for older files without statistics.

Cover short-series overlapping L0 files with an ordered latest-value regression.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* docs(mito): explain memory risk of splitting short L0 series

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* refactor: address snapshot export review feedback

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(cli): accept source errors in lazy snapshot contexts

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

---------

Signed-off-by: jeremyhi <fengjiachun@gmail.com>
(cherry picked from commit 18726c7602)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(prom): align remote read label matcher semantics (#9633)

* fix(prom): align remote read label matcher semantics

Signed-off-by: fys <fengys1996@gmail.com>

* chore: optimize comment of validate_matchers

* fix(prom): preserve existing binary label regex handling

Signed-off-by: fys <fengys1996@gmail.com>

* refactor: condition

* test(prom): simplify remote read matcher tests and clarify comments

* fix(prom): return binary remote read labels as UTF-8

* fix(prom): reject binary labels in remote read

* refactor(prom): align remote read null label handling with PromQL

Signed-off-by: fys <fengys1996@gmail.com>

* refactor(prom): simplify null handling in label matcher branches

Signed-off-by: fys <fengys1996@gmail.com>

* fix(prom): restrict remote read labels to string types

Signed-off-by: fys <fengys1996@gmail.com>

* test(prom): use string label in default value column test

Signed-off-by: fys <fengys1996@gmail.com>

* fix: code review

---------

Signed-off-by: fys <fengys1996@gmail.com>
(cherry picked from commit 4e8bc6df86)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(mito2): restore source splitting for cross-range series merges (#9818)

* fix: restore batched series merges across ranges

Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(mito2): split range sources before last-row selection

Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(mito2): avoid tiny splits for single-source cross-range merges

Signed-off-by: WenyXu <wenymedia@gmail.com>

---------

Signed-off-by: WenyXu <wenymedia@gmail.com>
(cherry picked from commit 27c531336a)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(mito2): pick TWCS L1 merges by closed units so compaction terminates (#9741)

* refactor(mito2): pick TWCS L1 merges by closed units

TWCS could keep rewriting L1 files without new writes. Overlap-only picks
could split their inputs into one more file than they read, and L1 file
reduction then rewrote up to 16 files to remove that single file. The
outputs, split by key, created new overlaps and the cycle repeated.

Replace the three global pick phases with one pick per window, scheduled by
class across windows. Every pick now decreases a termination measure when the
window receives no writes.

- Active windows only take merges that remove files. L1 files are grouped by
  conflicts and merged in key order, so size-split tails are merged together.
  A reduction rewrites at most 4 output files' worth of bytes per removed file.
- Inactive windows absorb L0 files, then take closed merges: no other file
  intrudes on the inputs' bounding box, so outputs cannot add overlaps. Each
  merge resolves conflicts or removes files, decreasing (conflicts, files).
  Files that do not take part in an overlap are not rewritten.
- Overlaps that only share a boundary key no longer count as conflicts, so a
  size-split sorted run does not look overlapping.
- Closed merges may take 4x the input limit to resolve a whole window once.
- Cache primary key ranges per pick instead of resolving them per comparison.
- Log the pick branch of each compaction output.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* docs(mito2): state the compactor assumptions behind TWCS termination

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* feat(mito2): resolve active window overlaps on manual compaction

A window that stops receiving writes keeps being the active window, whose
picks only remove files, so its overlaps were never resolved. Manual
compactions now pick every window as inactive, so a closed merge can resolve
them. Automatic compactions are unchanged.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* fix(mito2): defer overlap resolution in the window before the active one

Late data keeps landing in the window right before the active window. A
closed merge that resolves its overlaps early rewrites the whole window,
then the late data brings the overlaps back, and the balanced run check
keeps them there. Overlap-only merges now skip that window until it is
older. File-reducing merges and manual compactions are unchanged.

On the 12h/1GB out-of-order bench this reaches overlap depth 1.0 instead
of 2.08 at the same write amplification (3.01 vs 3.02).

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* refactor(mito2): only check earlier-starting files when closing units

Closing units only checks files that start before a unit ends, and the
overlap checks compare key bounds without cloning them.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* fix(mito2): keep settling windows from rewriting L1 files

The window before the active one still receives late data. Skipping only
its overlap-only merges was not enough: file-reducing merges of
conflicting files and mixed L0/L1 merges still rewrote its L1 files,
and a mixed merge could rewrite a nearly full output for each small late
file. A settling window now only merges L0 files among themselves and
conflict-free files; the window resolves its overlaps once it is older.

On the out-of-order bench, write amplification drops from 3.88 to 3.30
at 2GB/6h, 3.05 to 2.94 at 1GB/6h and 3.01 to 2.96 at 1GB/12h, with
overlap depth 1.0 in all three.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* fix(mito2): resolve late overlaps once a window is quiet

Late data that lands in a window older than the settling one is merged
into a small file covering the whole key range. Resolving it rewrites
the window's dominant sorted run, which the balanced run check refuses,
so the overlap stayed. Removing the check instead rewrites the window
for every late flush.

A window is quiet once the region has written at least as many rows as
the window holds since its newest file. Overlap merges in a quiet window
may rewrite a dominant run, so each quiet period pays for at most one
such rewrite. Sequences are per row and kept by compaction, so this
needs no state.

On the bench with late data up to 2 hours, mean overlap depth drops
from 1.67 to 1.17 at 2GB/6h and from 2.33 to 1.83 at 1GB/6h, at write
amplification 3.70 to 4.25 and 3.26 to 3.74; all inactive windows of
2GB/6h realtime reach depth 1 (write amplification 2.80 to 3.30). The
remaining depth is in the final active window.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* test(mito2): check closed merges of quiet and settling windows terminate

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* fix(mito2): split TWCS closures over the input limit

A file spanning the keys of a large sorted run joins every run file into one
closure. Once a closure held more files than one compaction accepts, closed
merges skipped it, so the window never merged those files again and late
files kept piling up.

Split such closures into single files, like active-window picks do, and
replace the closed check with a conflict bound: outputs only conflict with
files intruding on the inputs' box, so a merge is taken if `outputs · I`
does not exceed the conflicts its inputs had with other files. Intervals of
whole closures have no such conflicts and still need to be closed, so other
windows pick as before, and `(conflicts, files)` still strictly decreases.

Small run files and late files over a large run can be merged again. A late
file over a run that cannot shrink stays: resolving it would rewrite the
whole run.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* fix(mito2): address TWCS picker review findings

- Manual compactions may rewrite a dominant sorted run to resolve overlaps.
  The active window is never quiet, so manual compactions could not resolve
  its late-data overlaps before.
- Single-key intervals predict one output: the writer never splits a series,
  so large files of one series were never merged.
- The active window falls back to single files when no group of conflicting
  files qualifies, so a large file no longer keeps its small peers unmerged.
- Compute the settling window distance with checked_sub, which overflowed for
  windows at the timestamp limits.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* fix(mito2): count inner keys as TWCS conflicts and check balance per group

- A single key strictly inside another file's key range now conflicts, so
  a late file of one series inside a wide file is resolved like other
  overlaps. Files sharing only a boundary key still do not conflict.
  Intrusion covers the new pairs, which a randomized test checks.
- Check the dominant-run guard per group of conflicting files. Runs of
  separate groups never overlap, so pairing one group's run with another
  group's peer made an interval of two dominant runs look balanced.
- A window with unknown row counts, written before they were recorded, is
  not quiet: its threshold would drop to a single row.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* perf(mito2): index TWCS files by key to avoid quadratic grouping

Grouping a window's files compared every file with all files sharing its
time range, and closing groups and checking intrusions scanned the whole
window. A sorted run split by key shares one time range, so a window of N
such files took O(N²) checks without any conflict. The 1024-file fallback
only covers the sorted-run path, not these groups.

Index the files by lower primary key in a segment tree that keeps each
subtree's largest upper key and its time bounds. Grouping, closing, and the
conflict bound only check the files whose key and time ranges meet, so a
file of a key-sliced run only meets its neighbors. Conflicts among split
files are precomputed per file and counted per interval with a binary
search instead of pairwise.

On a run of 20,000 key-sliced files with 5 late files spanning all keys,
a closed merge pick takes ~62 ms instead of ~3.9 s and an active pick
~19 ms instead of ~3.5 s.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* test(object-store): retry transient S3 transport failures

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* fix(mito2): bound TWCS closed-merge conflict graph work

Budget the candidate files visited while building conflict units and split-unit adjacency. When the budget is exceeded, treat every file as a split unit and pick a bounded file reduction instead of materializing a quadratic graph.

The fallback candidate is admitted only through adds_no_conflicts, so outputs do not add conflict pairs and the (P, N) termination invariant still holds. Dense picks are logged with their own branch.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* fix(mito2): charge TWCS index nodes to the conflict-graph budget

The conflict-graph work budget only counted files that reached a callback, so a traversal with poor pruning could still visit a quadratic number of internal nodes without triggering the dense fallback. Charge every ExtentIndex node, including pruned nodes, and stop the traversal as soon as the shared budget is exhausted.

Add a regression test where the root straddles the query time but neither leaf matches, so a callback-only budget would never fire.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* fix(mito2): retry smaller TWCS dense compaction candidates

If a dense fallback candidate fails admission, try one single-output subinterval within it before giving up. Reuse the reduction guards and check both candidates against the full window's external conflicts, preserving bounded work and termination.\n\nCover the chain regression through both fallback unit constructions and reject one-output retries that introduce conflicts across time gaps.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* fix(mito2): compact shared primary-key boundaries

Count shared primary-key endpoints as conflicts and apply the same overlap rule to bounding-box intrusions. Existing compaction writers keep series intact, so fresh output ranges remain disjoint.

Align simulated output splits with the writer and cover boundary-only cleanup in inactive and manual windows, plus stable real output files after a subsequent TWCS request.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* fix(mito2): compact small L1 files in inactive windows

Allow bounded small-L1 reductions in settling and older inactive windows when normal picks cannot proceed. Preserve full-window conflict checks, large-file protection, and LastNonNull closure semantics.\n\nCover selection limits, termination, and real SST compaction with tombstones and reopen checks.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* docs(mito2): clarify TWCS unit grouping guarantees

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

---------

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>
(cherry picked from commit bb3f07b482)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix: reclaim HDFS temporary files left behind by a datanode crash (#9616)

* fix: reclaim HDFS temporary files left behind by a datanode crash

Signed-off-by: lczllx <2181719471@qq.com>

* fix(mito2): stop the temporary file sweep from starving GC jobs

Signed-off-by: lczllx <2181719471@qq.com>

* feat(mito2): add metrics for the temporary file sweep

Signed-off-by: lczllx <2181719471@qq.com>

* refactor(object-store): share one definition of the temporary file name

Signed-off-by: lczllx <2181719471@qq.com>

* fix(mito2): skip the temporary file sweep when no GC job is allowed

Signed-off-by: lczllx <2181719471@qq.com>

* refactor: gate the temporary file sweep behind the hdfs-object-store feature

Signed-off-by: lczllx <2181719471@qq.com>

* fix(mito2): sweep temporary files when a create opens an existing region

Signed-off-by: lczllx <2181719471@qq.com>

* docs(config): document the temporary file sweep in the GC settings

Signed-off-by: lczllx <2181719471@qq.com>

* refactor: align the temporary file sweep code with the surrounding style

Signed-off-by: lczllx <2181719471@qq.com>

---------

Signed-off-by: lczllx <2181719471@qq.com>
(cherry picked from commit 88cb2a986e)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(meta): fence datanode processes replaced by a newer process (#9878)

* fix(meta): fence datanode processes replaced by a newer process

Two datanode processes can run with the same node id, and metasrv
grants leases and delivers instructions by node id only. Every datanode
heartbeat already carries node_epoch, the time the process started.

The metasrv leader records the highest accepted epoch per node id under
__datanode_epoch/{node_id}, a prefix of the leader cache, and raises it
with compare-and-put. The heartbeat session checks each datanode request
with a non-zero epoch before the handler chain: a lower epoch gets a
reply without running the chain, so no lease, keep-alive, stats or
mailbox reply. An epoch more than 5 minutes ahead of the leader's clock
is not recorded and is fenced too.

Datanode pushers carry the epoch of their process: a lower epoch cannot
replace a higher one, and a stream removes only its own pusher.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(meta): keep the leader cache load and pusher updates coherent

A write made while the leader cache is disabled, as during the load at
leader start, went to the store without the cache noticing, so the load
could install a value read before the write. Such writes now create new
cache versions around the store call, and the load drops what it loaded
for a prefix if a write happened meanwhile. This also covers node
addresses, which heartbeats write during the load.

A stream's pusher that takes its first epoch is updated in place instead
of being replaced, so messages waiting on it are not cancelled, and the
connection gauge counts only a pusher that fills an empty slot.

The epoch check moves into the heartbeat service.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(meta): keep the leader cache coherent with writes while it is disabled

A write that started while the cache was disabled could commit after the
load had read the old value and the cache had been enabled, leaving the
old value cached. Writes now maintain the cache whether or not it is
enabled, as enabled writes already did: new versions around the store
call, then the key is updated or invalidated.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* test(meta): pin the epoch 0 exemption and pusher order, and update docs

The datanode test without an epoch now runs after another process has
recorded one, and the pusher test checks that a stream's first accepted
request receives what its handlers broadcast. The leader cache load no
longer claims that writes must not run while it loads, and the meta-srv
guide states that epoch 0 is not checked.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(meta): register a datanode pusher only after its first request passes

A datanode stream registered an epoch-0 pusher on its handshake, so an
older process's reconnecting stream could take an empty slot, or a newer
process's pusher before its first heartbeat, for about a round trip. A
datanode stream now registers its pusher on its first request that
passes the epoch check (epoch-0 requests after the handshake included),
still before the handlers run.

Pusher registration is logged only when the insert happens. Tests cover
a fenced stream removing its own pusher, a handshake not taking an empty
slot, and a KV error failing the request; two steps that could not fail
are removed.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(meta): check a datanode epoch again before replying

A request could pass the epoch check, wait in the handler chain while a
newer process of the node was recorded, and still return a region lease.
After the handlers run, the session checks the epoch again against the
cached record and, if a newer process has been recorded, replies as to a
fenced request and removes the stream's own pusher. This costs one more
read of the cached record per checked datanode request.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(meta): handle a datanode's requests one at a time

A request of an older process could pass the epoch check, wait in the
handlers while a newer process was admitted, and then keep the node
alive with its own address. Checking the epoch again after the handlers
only dropped the reply.

The heartbeat session now takes an in-memory lock for the node id
before checking a request that carries an epoch, and releases it after
the handlers, before the reply is sent. A newer process is therefore
admitted only after the handlers of an older request finish. The check
after the handlers is removed.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

---------

Signed-off-by: jeremyhi <fengjiachun@gmail.com>
(cherry picked from commit 6f5c064f54)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix: acknowledge writes only within the region's leader lease (#9904)

* fix: acknowledge writes only within the region's leader lease

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix: drop cached logical columns after a failed logical drop

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix: keep the prepared region for the lease check after the WAL append

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix: serialize a logical drop with fills of the logical column cache

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* test: check the drop lock and grant long leases deterministically

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* test: cover a bulk insert that loses its lease during the WAL append

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

---------

Signed-off-by: jeremyhi <fengjiachun@gmail.com>
(cherry picked from commit 91a59b82fb)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(mito): preserve SST lifetime across index replacement (#9950)

* fix(mito): preserve SST lifetime across index replacement

Share retirement and final purge across immutable index metadata views. Keep version-specific GC references until readers release them and match Parquet liveness by file identity.

Fixes #9944

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* refactor(mito): list-based SST purge and single FilePurger::remove_file

The final purge of a retired SST no longer depends on FileMeta: index
files of every version are discovered by listing
({region_dir}/index/{file_id}. prefix), so even when the last live view
is a stale one, the purge still removes all newer index files.

- SstLifetime is slimmed down to { deleted, file_purger }; the purge now
  runs in the last FileHandleInner::drop (Arc::strong_count == 1, which
  is race-free here since only inners of the same file hold the lifetime).
- FilePurger is merged back into a single remove_file(file_meta,
  is_delete) method; is_delete means the file was retired AND this is the
  last live view.
- LocalFilePurger deletes the SST and all its index files via the new
  delete_file_and_all_indexes helper (list before delete, so a listing
  failure leaves the file untouched).
- Remove the dead index_outdated flag and its accessors, which had no
  callers in the entire repo.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* fix(mito): elect final SST view release atomically

Track metadata views with an atomic counter instead of inspecting the lifetime Arc strong count. Concurrent destructors now elect exactly one final releaser while preserving the single remove_file interface.

Add a deterministic concurrent-drop regression covering retired and live files.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* refactor(mito): restore Arc-owned SST cleanup and clarify reference release

Restore the lifetime design from 835227dcbc, using the final Arc drop and latest metadata instead of manual view counting and per-SST index listing.

Rename FilePurger::remove_file to release_file_ref while retaining purge_file for final cleanup. Preserve and adapt the concurrent view-release regression test.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* refactor(mito): track SST references by file lifetime

Register one reference guard per logical SST lifetime and release it on final drop, including normal region close. Remove per-view release_file_ref and index_outdated handling.

Protect all index generations while their SST is referenced. Preserve the existing GC snapshot format by expanding the recorded index generation upper bound for older version-aware workers.

Update coverage for independent references, normal close, cross-region ownership, concurrent final cleanup and both GC listing modes.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* refactor(mito): release SST references directly on lifetime drop

Remove the redundant FileReferenceGuard and let SstLifetime register and unregister its file reference directly. Preserve unconditional reference release on normal close and retirement-only purge.

Expose the reference manager through FilePurger and adapt reference manager tests without changing GC or snapshot behavior.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* test(mito): verify SST cleanup with real storage and isolate GC policy cases

Replace the recording-only concurrent purge test with filesystem assertions using LocalFilePurger and shared fixtures. Keep intermediate-view retention and retired versus live file cleanup coverage without claiming exhaustive thread interleavings.

Separate GC reference-policy cases from reader-release ordering and reuse their setup. Production code remains unchanged.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* style(mito): format SST lifetime changes

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* fix(mito): reclaim obsolete indexes by reader version

Track exact index-version references per metadata view and restore version-specific Puffin liveness in both GC paths. Keep file-level Parquet protection and shared SST lifetime cleanup, without expanding snapshots into unreferenced index generations.

Include old readers of imported files even when the source region is not open locally. Add real-storage GC coverage for reader release and lingering while retaining Parquet and the current index.

Validated workspace checks with default and all features, all-target all-feature Clippy with warnings denied, and all 1853 mito2 tests.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

---------

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>
(cherry picked from commit 17ed988f49)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(mito2): coordinate range cache invalidation and singleflight reads (#9935)

* fix(mito2): invalidate retired SST range results and coalesce loads

Coordinate decoded range loads by their full scan key, bound shared work, and prevent late fills after SST retirement. Invalidate range results for local purge and object-store GC while preserving old snapshot reads.

Fixes #9934

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* fix(mito): preserve SST lifetime across index replacement

Share retirement and final purge across immutable index metadata views. Keep version-specific GC references until readers release them and match Parquet liveness by file identity.

Fixes #9944

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* refactor(mito): list-based SST purge and single FilePurger::remove_file

The final purge of a retired SST no longer depends on FileMeta: index
files of every version are discovered by listing
({region_dir}/index/{file_id}. prefix), so even when the last live view
is a stale one, the purge still removes all newer index files.

- SstLifetime is slimmed down to { deleted, file_purger }; the purge now
  runs in the last FileHandleInner::drop (Arc::strong_count == 1, which
  is race-free here since only inners of the same file hold the lifetime).
- FilePurger is merged back into a single remove_file(file_meta,
  is_delete) method; is_delete means the file was retired AND this is the
  last live view.
- LocalFilePurger deletes the SST and all its index files via the new
  delete_file_and_all_indexes helper (list before delete, so a listing
  failure leaves the file untouched).
- Remove the dead index_outdated flag and its accessors, which had no
  callers in the entire repo.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* fix(mito): elect final SST view release atomically

Track metadata views with an atomic counter instead of inspecting the lifetime Arc strong count. Concurrent destructors now elect exactly one final releaser while preserving the single remove_file interface.

Add a deterministic concurrent-drop regression covering retired and live files.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* refactor(mito): restore Arc-owned SST cleanup and clarify reference release

Restore the lifetime design from 835227dcbc, using the final Arc drop and latest metadata instead of manual view counting and per-SST index listing.

Rename FilePurger::remove_file to release_file_ref while retaining purge_file for final cleanup. Preserve and adapt the concurrent view-release regression test.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* refactor(mito): track SST references by file lifetime

Register one reference guard per logical SST lifetime and release it on final drop, including normal region close. Remove per-view release_file_ref and index_outdated handling.

Protect all index generations while their SST is referenced. Preserve the existing GC snapshot format by expanding the recorded index generation upper bound for older version-aware workers.

Update coverage for independent references, normal close, cross-region ownership, concurrent final cleanup and both GC listing modes.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* refactor(mito): release SST references directly on lifetime drop

Remove the redundant FileReferenceGuard and let SstLifetime register and unregister its file reference directly. Preserve unconditional reference release on normal close and retirement-only purge.

Expose the reference manager through FilePurger and adapt reference manager tests without changing GC or snapshot behavior.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* test(mito): verify SST cleanup with real storage and isolate GC policy cases

Replace the recording-only concurrent purge test with filesystem assertions using LocalFilePurger and shared fixtures. Keep intermediate-view retention and retired versus live file cleanup coverage without claiming exhaustive thread interleavings.

Separate GC reference-policy cases from reader-release ordering and reuse their setup. Production code remains unchanged.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* style(mito): format SST lifetime changes

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* fix(mito2): use crate-rooted range cache imports

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* fix(mito2): account for range cache delivery lifetime

Keep completed range results charged while watch subscribers or replay streams retain them. Cache entries keep weak delivery references, allowing cache-only ownership to release the delivery reservation. Cache hits reuse or nonblockingly acquire the reservation, falling back when the budget is exhausted.

Resize input reservations to completed-result estimates and cover successive slow consumers, cache-only handoff, hit sharing, invalidation, admission rejection, and cancellation with regression tests.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

---------

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>
(cherry picked from commit 44df6e5ded)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(meta): exclude route followers from failover targets (#10001)

* fix(meta): exclude route followers from failover targets

Failover target selection did not exclude the region's route followers.
The selector could keep picking a follower, the submit rejected it as a
peer conflict, and the region never failed over.

Group the failed regions by their followers and exclude them per group.
A group without a candidate is skipped instead of failing every region of
the datanode.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* test: check failover continues after a group without candidates

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(meta): keep failover going when follower routes can't be read

Fall back to selecting without follower exclusion when the routes can't
be read, log skipped groups with their followers at warn level, and test
a region without a route.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* refactor(meta): select failover targets per exclusion set in select_peers

generate_failover_tasks now only reads the route followers of the regions.
select_peers resolves the affinity and the unschedulable datanodes once,
then selects per group of regions that exclude the same datanodes.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* refactor(meta): drop a comment that repeats the code

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* refactor(meta): tighten failover target selection

Name the route followers after the parameter they fill, read the routes
with unwrap_or_else, and log a group without a candidate as an error, as
the caller did before.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(meta): let the region-stat-aware selector balance the whole batch

The region-stat-aware selector now receives each region's excluded peers
along with the whole batch, so it can still balance load across the
regions while keeping a region off its own followers. A region without a
candidate is left out of its output instead of failing the others. The
selectors driven by SelectorOptions keep selecting per group of regions
that exclude the same datanodes.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* test: name the batch type of the stat-aware recorder

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* refactor(meta): treat a selector without candidates the same in both arms

Both selector arms now place what they can and treat NoEnoughAvailableNode
as nothing placed; the stat-aware selector's contract says so. Rename its
parameter to exclude_peer_ids_by_region, check for unplaced regions in
linear time, and describe what the code does in the comments.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* test: keep the stat-aware recorder's placements off excluded peers

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

---------

Signed-off-by: jeremyhi <fengjiachun@gmail.com>
(cherry picked from commit 62ebf71f90)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(log-store): make raft-engine periodic WAL sync actually fsync (#9715)

* fix(log-store): make raft-engine periodic WAL sync actually fsync

On raft-engine 0.4.2, Engine::sync() writes an empty LogBatch and
write() returns before fsync (tikv/raft-engine#395). The periodic
sync task therefore never durable-flushed acknowledged WAL data.

Work around by writing a minimal non-empty marker with sync=true so
the fsync path is reached until a crates.io release includes
tikv/raft-engine#400.

Fixes #9712

Signed-off-by: Yvan Wang <131545713+BootstrapperSBL@users.noreply.github.com>

* perf(log-store): reuse LogBatch across periodic WAL syncs

Keep the periodic sync task's LogBatch across ticks instead of
allocating a fresh one (4 KiB encode buffer plus item vec) every
period. A successful Engine::write drains the batch but retains its
buffers; after a failed write or a panicked blocking task the batch is
discarded and recreated on the next tick, since it may be left
partially encoded.

Keep the marker put rather than deleting an empty key: the put
overwrites a single key, whereas every delete would become a recovery
tombstone that raft-engine keeps in memory while replaying append
files.

Also document that the marker value must not decode as a NamespaceImpl
(the marker key lies inside the range scanned by list_namespaces), add
a test covering batch reuse and namespace listing, and apply rustfmt.

Refs #9712

Signed-off-by: Yvan Wang <131545713+BootstrapperSBL@users.noreply.github.com>

---------

Signed-off-by: Yvan Wang <131545713+BootstrapperSBL@users.noreply.github.com>
(cherry picked from commit 180e2716fc)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(catalog): propagate frontend process-list failures (#9999)

* fix(catalog): propagate frontend process-list failures

Return the existing invocation error when a selected frontend cannot list processes, so a successful response cannot omit that peer.

Signed-off-by: Byte-Naut <Byte-Naut@proton.me>

* test(frontend): cover remote process-list errors and aggregation

Exercise real metadata discovery and gRPC listeners for a missing management service and successful catalog-scoped aggregation.

Signed-off-by: Byte-Naut <Byte-Naut@proton.me>

---------

Signed-off-by: Byte-Naut <Byte-Naut@proton.me>
(cherry picked from commit 3d2b73762a)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(log-store): skip records with unparsable keys instead of aborting the read (#9749)

* fix(log-store): skip records with unparsable keys instead of aborting the read

Signed-off-by: lczllx <2181719471@qq.com>

* fix(log-store): keep undecodable RecordMeta keys fatal

Signed-off-by: lczllx <2181719471@qq.com>

---------

Signed-off-by: lczllx <2181719471@qq.com>
(cherry picked from commit 5f0e22171e)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(meta): register failure detectors for regions switched before a rollback (#9989)

* fix(meta): register failure detectors for regions switched before a rollback

A region migration switches the routes of its tables one table at a
time. If it aborts after some tables were switched, those regions are
already served by the target, but the rollback deregistered the
target's failure detectors for every region and registered only the
source's. The target's next heartbeat registers them again, so if the
target stopped before sending one, nothing detected its failure and
the switched regions stayed unavailable until the metasrv leader
changed or the target came back.

The rollback now collects, from the routes it already reads, the
regions whose leader is the target and resets the target's failure
detectors for them at the end. It also skips a table whose route no
longer exists, so a table dropped meanwhile no longer fails the
rollback before the detectors are handled.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* test(meta): roll back a downgrading leader in the rollback detector test

The table that was not switched yet keeps a downgrading leader in a real
abort, so the test now builds it that way and checks that the rollback
clears it.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(meta): monitor regions switched before a rollback only on the target

The rollback registered the source's failure detectors for every region
of the migration, including the regions already switched to the target.
Those regions are served by the target after the rollback, so their
source detectors watched the wrong peer: when one fired, the failover
found the leader had changed and only removed it. The rollback now
registers the source's detectors only for the regions that were not
switched, and keeps resetting the target's detectors for the others.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* refactor(meta): rename reset_failure_detectors_for_candidate_regions to reset_failure_detectors

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

---------

Signed-off-by: jeremyhi <fengjiachun@gmail.com>
(cherry picked from commit 05536cd7f5)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix: ignore stale leader grants after a region is downgraded (#9987)

* fix: ignore stale leader grants after a region is downgraded

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix: compare stale leader grants with the longest lease seen

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix: treat a lease too long to store as unknown

A lease longer than a u64 of nanoseconds saturated to a shorter one, so the
derived send time could be later than the real one. Now such a lease makes
the send time unknown, which counts as earlier than any gate.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix: address review comments on stale leader grants

Scope the early send time claim to after a lease has been seen, drop the
unused role parameter of demote, clarify a test comment and check that a
fresh grant still makes a reopened region writable.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* refactor: compare stale leader grants with the heartbeat send time

Pass the send time of the heartbeat behind a lease to set_region_role_with_lease,
and return whether the role was applied. Mito compares the send time with the
grant gate directly, so it no longer derives it from the longest lease seen.
The datanode finalizes leadership only when the grant was applied.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* test: cover stale grants through the datanode and repeated downgrades

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* refactor: keep leader grant rules in one region method

Apply a role grant in MitoRegion::apply_role_grant, which renews the lease
and decides whether the role changes; the engine only looks up the region.
Rename the gate to downgrade_started_at and demote to begin_downgrade, keep
the field private, and copy it on reopen through
inherit_downgrade_state_from.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* refactor: drop a needless borrow

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* refactor: keep lease concepts in the engine and role ordering in the region

ManifestContext, which owns the role, records when the role was last
demoted and promotes a region only for a promotion issued after that. The
time starts when the context is created, so a reopened region ignores
grants for heartbeats sent before the reopen, without copying state. The
engine translates leases: it renews the deadline and promotes with the
heartbeat send time. MitoRegion keeps no grant state.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix: keep the data region of a metric region off a grant its metadata region ignores

The metric engine applies a grant to the metadata region first and stops
if it is ignored, so the data region, whose role the engine reports, never
becomes a leader that skips finalization. Also describe what promoting
returns.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* docs: describe when a lease grant is ignored and flatten the finalize check

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* refactor: move lease types out of the region and the Leader deadline into ManifestContext

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* refactor: name the promotion fence after the downgrade it records

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

---------

Signed-off-by: jeremyhi <fengjiachun@gmail.com>
(cherry picked from commit 89a5df3141)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(mito2): preserve live index files across remote compactions (#10046)

fix(mito2): reuse executor-owned compaction index resources

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>
(cherry picked from commit c7ea80558f)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(datanode): pick a node epoch above the recorded one (#9984)

* fix(datanode): pick a node epoch above the recorded one

The metasrv fences a datanode whose node epoch is lower than the
highest one it has recorded for the node id. A datanode used its start
time as its epoch, so after a restart on a host whose clock is behind
the previous process's, or after the clock stepped back, the new
process picked a lower epoch and was fenced for good: no region lease,
no keep-alive.

Before it creates the heartbeat task, the datanode now reads the
recorded epoch through its metadata backend and uses one more than it
if its start time is not higher. The node info still reports the local
start time. A failed read fails the startup, like the other metadata
reads.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* test(datanode): tighten the node epoch tests

Use a lower record that leaves room below the start time, so the test
fails if the start time is ignored, and a nonzero node id, so it fails
if the node id is ignored. Clarify the doc comment of the epoch choice.

This assumes the previous process of a node id has stopped before a new
one starts.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

---------

Signed-off-by: jeremyhi <fengjiachun@gmail.com>
(cherry picked from commit c40e86c653)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(auth): preserve physical metric table targets (#10084)

* fix(auth): preserve physical metric table targets

Signed-off-by: shuiyisong <xixing.sys@gmail.com>

* fix: nit

Signed-off-by: shuiyisong <xixing.sys@gmail.com>

---------

Signed-off-by: shuiyisong <xixing.sys@gmail.com>
(cherry picked from commit c17a5e09f7)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix: include peer context in distributed query errors (#10118)

Signed-off-by: WenyXu <wenymedia@gmail.com>
(cherry picked from commit 8deb3872b3)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(auth): redact inline user-provider credentials (#10115)

* fix(auth): redact inline user-provider credentials

Signed-off-by: shuiyisong <xixing.sys@gmail.com>

* fix(auth): close user-provider redaction gaps

Signed-off-by: shuiyisong <xixing.sys@gmail.com>

---------

Signed-off-by: shuiyisong <xixing.sys@gmail.com>
(cherry picked from commit adf29740b2)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix: register trigger procedure loaders through DdlManager (#10123)

Add a loader registration hook to TriggerDdlManager and invoke it
  before procedure recovery. Test that registration reaches the
  configured trigger DDL manager.

Signed-off-by: fys <fengys1996@gmail.com>
(cherry picked from commit b6cf3384dc)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(time): parse zero-hour timezone offsets correctly (#10091)

fix(time): preserve zero-hour timezone offset signs

Signed-off-by: Minh Vu <vuhoangminh97@gmail.com>
(cherry picked from commit 25003cea16)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(meta): invalidate all datanode tables on address change (#9986)

When a datanode restarts with a new address, metasrv invalidated cached
table routes only for the tables in that heartbeat's region stats. The
first heartbeat after a restart often reports few or no regions (e.g.
with init_regions_in_background), so frontends kept dialing the old
address until metadata_cache_ttl expired.

Read the previous address via prev_kv when saving it, and when it
changed (or a failed save of the same epoch is retried) invalidate
every table registered for the datanode in __dn_table.

Signed-off-by: Floris Smit <florissmit10@gmail.com>
Co-authored-by: Weny Xu <wenymedia@gmail.com>
(cherry picked from commit 351f4207b4)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(function): require ADMIN statement privileges to execute admin functions (#10120)

* fix(function): require ADMIN statement privileges to execute admin functions

Read-only accounts could execute the admin UDFs (flush_table,
compact_table, flush_flow, gc_regions, migrate_region, reconcile_*,
build_index, ...) through ordinary SELECT statements: SELECT is a
read-only statement so it passes the statement-level privilege check,
and the functions were only guarded by the greptime-catalog scope
check inside the UDF. That let read-only users trigger WAL flushes,
manifest version bumps, compactions, flow-sink writes and, in
distributed mode, region GC/migration and SST deletion.

Wrap the privilege check into the admin_fn macro itself, next to the
existing greptime-catalog check: before executing, every #[admin_fn]
function now requires the same privileges as its ADMIN statement. The
check synthesizes the PermissionReq::SqlStatement request an
ADMIN <fn>(...) statement produces and evaluates it with the same
PermissionCheckerRef plugin the frontend installs, so query invocation
(SELECT <fn>(...), EXPLAIN, views, flows) and the ADMIN statement path
are gated identically — in particular read-only accounts are denied
with PermissionDenied (7006), while write-privileged users keep both
invocation styles. With auth disabled (no permission checker), the
functions stay invocable by everyone, exactly like ADMIN statements.
The denial is wrapped as External(BoxedError) so the 7006 status code
survives both the query execution path and the admin statement
executor.

Regression tests: unit tests for the check (no-checker allows,
read-only denied 7006, read-write allowed), an end-to-end test over
the macro-generated flush_table UDF, and an integration test where a
read-only account is denied 7006 on all 13 functions from SELECT while
a read-write account keeps SELECT and ADMIN FLUSH_TABLE working.

Signed-off-by: Ning Sun <sunning@greptime.com>

* fix(function): expose admin privilege check to downstream macros

Signed-off-by: Ning Sun <sunning@greptime.com>

---------

Signed-off-by: Ning Sun <sunning@greptime.com>
(cherry picked from commit b21b1cdb69)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(catalog): use advertised peer address as process identity (#10153)

* fix(catalog): use advertised peer address as process identity

The process manager identified the local frontend by its public gRPC
address, while metasrv discovers peers by the address advertised in the
heartbeat (the internal gRPC address when configured). With separate
public and internal listeners, list_all_processes failed to exclude the
local node, queried its own management service as a remote frontend, and
appended the local registry again, so every local query was listed twice.
The same mismatch broke routing of KILL to other frontends.

Identify a frontend by the address it advertises to metasrv:
- add FrontendOptions::peer_addr, shared by the heartbeat and the process
  manager, and FrontendOptions::create_process_manager to wire it once;
- use the advertised address as the process identity, for self-exclusion
  in list_all_processes, and for local/remote routing in kill_process.

Process ids now carry the address other frontends use to reach the node.
When no internal gRPC listener is configured this is the public gRPC
address, so the previous behavior is unchanged. The internal listener is
the only one serving the frontend management service, so process ids stay
routable.

The new distributed regression tests exercise separate public and internal
addresses: the local query must be listed exactly once while remote
frontends are still listed, and KILL must route to a remote frontend.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

* test: keep local peer address reserved in kill-routing test

The test reserved the local frontend address with a temporary listener that
was dropped immediately, then reserved the remote address separately. The
kernel could assign the same port to the remote frontend, so the remote kill
request would take the local branch and fail to find the remote process.

Bind the local address once and keep the listener alive for the whole test so
the local and remote frontend identities cannot collide.

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>

---------

Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>
(cherry picked from commit 557843dd68)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(mito): use stored recovery-set bytes for manifest size (#10142)

* fix(mito): track stored bytes for manifest recovery

Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(mito): retain manifest sizes on checkpoint and sync

Signed-off-by: WenyXu <wenymedia@gmail.com>

* test(mito): cover logical manifest size accounting

Signed-off-by: WenyXu <wenymedia@gmail.com>

* docs(mito): clarify manifest size retention invariants

Signed-off-by: WenyXu <wenymedia@gmail.com>

---------

Signed-off-by: WenyXu <wenymedia@gmail.com>
(cherry picked from commit d7036df73c)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(grpc): propagate cancellation for read query execution (#10166)

* x

* fix(grpc): restrict cascading cancellation to read query variants

Signed-off-by: luofucong <luofc@foxmail.com>

---------

Signed-off-by: luofucong <luofc@foxmail.com>
(cherry picked from commit 2cb473607d)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(datatypes): escape object keys when rendering jsonb as text (#10112)

* fix(datatypes): escape object keys when rendering jsonb as text

Signed-off-by: Yvan Wang <131545713+BootstrapperSBL@users.noreply.github.com>

* fix(datatypes): skip unicode normalization for escaped jsonb keys

Signed-off-by: Yvan Wang <131545713+BootstrapperSBL@users.noreply.github.com>

* refactor(datatypes): serialize jsonb to text directly with serde_json

Serialize the binary jsonb with serde_json::to_string(&RawJsonb) so that
both object keys and string values are escaped by serde_json. This drops
fix_unicode_point, its regex and the hand-written key escaping. Errors are
propagated instead of falling back to lossy raw bytes.

Characters such as U+2028 are now emitted as-is, which is still valid
JSON, so the affected sqlness result lines are regenerated.

Signed-off-by: Yvan Wang <131545713+BootstrapperSBL@users.noreply.github.com>

* fix(datatypes): accept JSON text bytes in jsonb_to_string

Some write paths (COPY FROM csv/json/parquet, JSON bound as text via
MySQL) store JSON text instead of binary jsonb in JSON columns. Re-encode
such bytes as jsonb before serializing with serde_json, so they still
render (properly escaped) instead of failing with InvalidJsonb.

Signed-off-by: Yvan Wang <131545713+BootstrapperSBL@users.noreply.github.com>

---------

Signed-off-by: Yvan Wang <131545713+BootstrapperSBL@users.noreply.github.com>
(cherry picked from commit ec1ad1f15b)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(mito2): share bulk memtable compaction dispatcher across workers (#10022)

Each region worker built its own MemtableBuilderProvider, and every
provider created its own CompactDispatcher. The limit on background
bulk memtable compactions was therefore num_workers times
max_background_compactions, while SST compaction uses one
engine-wide pool of max_background_compactions.

Create one dispatcher in WorkerGroup::start (and start_for_test),
pass it to every worker, and make MemtableBuilderProvider::new take
it instead of creating its own. SST and memtable compaction keep
separate budgets of the same size.

Signed-off-by: luckys <190197589+luckysxx@users.noreply.github.com>
Co-authored-by: luckys <190197589+luckysxx@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: dennis zhuang <killme2008@gmail.com>
(cherry picked from commit 9f7cedf58f)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(mito): decode cached manifest payloads (#10179)

* test(mito2): cover compressed manifest cache recovery

Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(mito2): decode cached manifest object payloads

Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(mito2): address manifest cache review feedback

Signed-off-by: WenyXu <wenymedia@gmail.com>

---------

Signed-off-by: WenyXu <wenymedia@gmail.com>
(cherry picked from commit 4263f5fc01)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix: prioritize region ALTER over queued edits (#10193)

* fix: prioritize region ALTER over queued edits

Signed-off-by: evenyag <realevenyag@gmail.com>

* test: use valid edits in ALTER priority regression

Signed-off-by: evenyag <realevenyag@gmail.com>

* refactor: make region edit dispatch outcomes explicit

Signed-off-by: evenyag <realevenyag@gmail.com>

* fix: reject queued alters and edits when dropping regions

Signed-off-by: evenyag <realevenyag@gmail.com>

* test: cover queued ALTER handoff before region edits

Signed-off-by: evenyag <realevenyag@gmail.com>

---------

Signed-off-by: evenyag <realevenyag@gmail.com>
(cherry picked from commit ae422ef465)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(meta-srv): invalidate leader cache on transaction errors (#10219)

Signed-off-by: WenyXu <wenymedia@gmail.com>
(cherry picked from commit 4688fa4be8)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(mito2): avoid reporting reader filter metrics twice (#10222)

* perf(mito2): cache fixed-label reader metric counters

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(mito2): report reader filter metrics only once

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

---------

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
(cherry picked from commit 5d88d283e5)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* perf(mito2): reuse static manifest checks across row groups (#10220)

* perf(mito2): reuse static manifest checks for cached readers

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* test(mito2): drop redundant pruner test and release entry lock early

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* perf(mito2): cache static manifest keep verdicts directly

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

---------

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
(cherry picked from commit c844d44721)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix: adapt v1.3.1 backports to release interfaces

Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix!: enforce sparse primary key schema (#10020)

* fix: preserve required sorting for ordinary sparse scans

Signed-off-by: shuiyisong <xixing.sys@gmail.com>

* fix: enforce sparse primary key schema

Signed-off-by: shuiyisong <xixing.sys@gmail.com>

* test: fix

Signed-off-by: shuiyisong <xixing.sys@gmail.com>

---------

Signed-off-by: shuiyisong <xixing.sys@gmail.com>
(cherry picked from commit efed847ca6)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix: test (#10149)

fix: fixture

Signed-off-by: shuiyisong <xixing.sys@gmail.com>
(cherry picked from commit 4633c37dbb)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix: tolerate invalid UTF-8 in JSON log ingestion (#10245)

* fix: tolerate invalid UTF-8 in JSON ingestion

Signed-off-by: shuiyisong <xixing.sys@gmail.com>

* fix: tolerate invalid UTF-8 in NDJSON ingestion

Signed-off-by: shuiyisong <xixing.sys@gmail.com>

* chore: upgrade simd json

Signed-off-by: shuiyisong <xixing.sys@gmail.com>

* feat: add server default for ignore_errors

Signed-off-by: shuiyisong <xixing.sys@gmail.com>

---------

Signed-off-by: shuiyisong <xixing.sys@gmail.com>
(cherry picked from commit 0cb70d4f84)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* test: simplify backported JSON recovery cases

Signed-off-by: WenyXu <wenymedia@gmail.com>

* feat(otlp): add v1 schema for auto-created OTLP log tables (#10207)

* feat(otlp): add v1 schema for auto-created OTLP log tables

New tables created by direct OTLP logs ingestion get a v1 built-in schema:
service_name tag from resource service.name ahead of scope_name in the
primary key, a skipping index on trace_id, and event_name and
observed_timestamp fields. The table is marked with
table_data_model=greptime_log_v1. Existing tables without the marker keep
the legacy schema.

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* refactor(otlp): share resource service.name parsing between logs and traces

Log tables now derive service_name with the same rules as trace spans, so
both signals agree on the value used to correlate them. The integration test
writes twice to cover the existing v1 table path.

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

---------

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
(cherry picked from commit 2714687ab7)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(promql): bound scatter time filter expression depth (#10272)

Signed-off-by: evenyag <realevenyag@gmail.com>
(cherry picked from commit 361f3595db)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(query): use current partition columns in prepared plans (#10244)

A prepared statement keeps its logical plan, and that plan holds the
table as it was at prepare time. The analyzer reads the partition
columns from that table while the regions to scan come from the current
route. After a table's partition columns change, the analyzer can
consider a query safe to push down, so each region runs the whole query
and the frontend concatenates their results. The same happens to the
source of a prepared INSERT ... SELECT, which then stores wrong values.

Before executing a kept plan, compare each scanned table's partition
columns with the current table found by id. Scans of a table whose
columns changed get the current table when the schema is unchanged.
Otherwise they keep their schema, because a scan's projection holds
indices into it, and report the current columns as absent from it, so
the query keeps merging on the frontend.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>
(cherry picked from commit 297a343fd4)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(time): avoid i32 overflow when converting an out-of-range date (#10299)

Signed-off-by: Arpit Jain <arpitjain099@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
(cherry picked from commit 402e4a97fc)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* feat: update dashboard to v0.14.5 (#10270)

Co-authored-by: dennis zhuang <killme2008@gmail.com>
(cherry picked from commit 4c4c8a58fd)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(mito2): keep rows written during staging in the WAL (#10253)

* fix(mito2): keep rows written during staging in the WAL

A flush during staging committed its SSTs to the staging manifest, then
dropped the memtable and released the WAL. If staging did not end
successfully those rows were gone. A flush that committed after staging
had ended lost its rows even though staging succeeded.

Staging regions no longer flush. They are skipped by memory-pressure,
periodic and region-limit flushes, and their writes are rejected instead
of queued at twice the region limit or when the node's staging memtables
reach half of the engine's flush limit. A manual or repartition flush on
a staging region with data gets a retryable error; the migration
pre-flush and the WAL prune flush reply success without flushing. Closing
a staging region leaves staging first, so skip-WAL rows are flushed to
the normal manifest.

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(mito2): keep accounting and closing staging regions during staged edits

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(mito2): track staged edits without relying on the partition info

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* test(mito2): synchronize staging close tests with worker admission

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* test(mito2): observe the manifest lock wait when closing staging regions

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(mito2): detect staged edits through the staging partition info

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* docs(mito2): record that staging regions must not flush

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(mito2): do not restore staging after a close abandons the edit

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(mito2): reject region edits during a close-time flush

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* style(mito2): format the staging tests

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(mito2): reject staging edits once staging is abandoned

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* fix(mito2): bind apply-staging edits to their partition expression

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

* docs(mito2): note that staging regions pin their topic's prune point

Signed-off-by: jeremyhi <fengjiachun@gmail.com>

---------

Signed-off-by: jeremyhi <fengjiachun@gmail.com>
(cherry picked from commit 8da533c35f)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* perf(query): push column projections through MergeScan (#10221)

* perf(query): push column projections through MergeScan

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(query): retain empty projections and refresh sqlness plans

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* test(query): update TSID join plans after projection pushdown

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

---------

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
(cherry picked from commit 16123d806b)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* test: adapt staging and prepared-plan regressions to v1.3

Signed-off-by: WenyXu <wenymedia@gmail.com>

* test: regenerate MergeScan semi-join snapshot on v1.3

Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix: restrict JSON retries to invalid UTF-8 errors (#10302)

* fix: restrict JSON retries to invalid UTF-8 errors

Signed-off-by: shuiyisong <xixing.sys@gmail.com>

* test: isolate UTF-8 retry counter assertions

Signed-off-by: shuiyisong <xixing.sys@gmail.com>

---------

Signed-off-by: shuiyisong <xixing.sys@gmail.com>
(cherry picked from commit 09c0291575)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(ci): prepare Ubuntu build runners and default EC2 disks to 500 GiB (#10156)

* ci: share Linux runner dependency setup for builds and benchmarks

Signed-off-by: WenyXu <wenymedia@gmail.com>

* ci: configure EC2 build runner volumes with a 500 GiB default

Signed-off-by: WenyXu <wenymedia@gmail.com>

---------

Signed-off-by: WenyXu <wenymedia@gmail.com>
(cherry picked from commit c02e1f58af)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(ci): increase nightly JSONBench runner root volume (#10146)

Signed-off-by: luofucong <luofc@foxmail.com>
(cherry picked from commit e80cf9a9ae)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(datatypes): render jsonb stored as JSON text without panics or wrong values (#10197)

* fix(datatypes): decode jsonb before rendering it as text

jsonb_to_string serialized the bytes through RawJsonb's Serialize impl,
which panics with an out-of-range slice index on JSON text such as
"hello world" or "        null" (stored by COPY FROM and MySQL text
parameters) and on truncated jsonb. Its text fallback was also stricter than
the extended parser reads used before.

Decode with jsonb::from_slice, as reads did before #10112, then write the
value out with serde_json escaping for keys and strings.

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* perf(datatypes): keep streaming jsonb serialization in jsonb_to_string

Decoding into a jsonb::Value tree allocates per array, object and key on
every JSON cell returned over PostgreSQL and MySQL. Go back to serializing
the raw bytes with RawJsonb's Serialize impl, and bump jsonb to a rev whose
Serialize returns an error instead of panicking on out-of-range payloads
(GreptimeTeam/jsonb#2). Bytes that fail to serialize are parsed as JSON
text with the extended parser, as jsonb::from_slice does.

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* test(datatypes): pin text values that used to read back as false

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* chore: bump jsonb to reject scalar headers with non-zero length bits

JSON text over ~2 MB could still pass the payload bounds check and read back as a wrong value (GreptimeTeam/jsonb#2).

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(datatypes): report the text parse error when JSON bytes are neither jsonb nor text

Also pin jsonb_to_serde_json on JSON text scalars that used to decode as false.

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* chore: pin jsonb to the merged commit on main

GreptimeTeam/jsonb#2 reached main through GreptimeTeam/jsonb#3.

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

---------

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
(cherry picked from commit ddc16bafe8)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* revert(mito2): exclude range cache singleflight changes from v1.3.1

Revert b33634417e (#9935) from the release candidate. Preserve the separate SST lifetime fix (#9950). The range cache admission experiment did not restore Q2 performance.

Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix: redact credential table options in user-visible output (#10346)

information_schema.tables.create_options (and SHOW TABLE STATUS) printed
object store credentials of external tables verbatim, and SHOW CREATE TABLE
masked only access_key_id and secret_access_key. Mask all four credential
keys in both paths, and sort extra options in create_options by key.

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
(cherry picked from commit 62dad907d0)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* perf(mito2): prefilter single-column dynamic filters (#10340)

* perf(mito2): prefilter dynamic join membership

Read the narrow dynamic-membership column before the full projection and use a stable per-row-group mask to select rows. The live filter above the scan stays authoritative; bounds-only and unsupported shapes keep all rows.

Signed-off-by: discord9 <discord9@163.com>

* test: cover dynamic prefilter cache and generations

Prove the prefilter-result cache keeps the static mask but never reuses a dynamic mask across producer generations. Document why a superseded dynamic mask is discarded rather than assumed monotone.

Signed-off-by: discord9 <discord9@163.com>

---------

Signed-off-by: discord9 <discord9@163.com>
(cherry picked from commit c937e3997b)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(servers): authorize HTTP database overrides (#10378)

* fix(servers): authorize HTTP database overrides

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(servers): update the HTTP output benchmark handler call

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(servers): authorize Prometheus database overrides

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(servers): retain authorized label lookup scope

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

---------

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
(cherry picked from commit 5d88de5290)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(config)!: redact credentials from diagnostic TOML (#10381)

* fix(config): redact credentials from diagnostic TOML

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(config): redact credentials at their owning fields

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(config): cover skipped SAS tokens and export logs

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(cli): mask SAS tokens before overlapping account names

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(cli): redact SAS tokens before overlapping account fields

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(cli): mask containing credentials before shorter values

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

---------

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
(cherry picked from commit ca1b8c7c98)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* feat: update dashboard to v0.14.6 (#10365)

(cherry picked from commit 2e1ce9808a)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* feat(query): serialize remote dynamic membership (#10341)

* feat(query): serialize remote dynamic membership

Recover single-column UTF-8 build-key membership from InList and HashTableLookupExpr producers and send it to remote datanodes using the existing PhysicalInListNode. Non-monotone or unsupported subtrees widen to true; the receiver keeps the live filter authoritative.

Signed-off-by: discord9 <discord9@163.com>

* refactor(query): send lookup membership as a key list

Serialize a HashTableLookupExpr as the existing PhysicalInListNode and preserve the rest of the expression tree. Remove per-subtree monotone and true rewrites; only an unrepresentable lookup or an over-budget payload falls back at the whole-payload level.

Signed-off-by: discord9 <discord9@163.com>

* docs: tighten narrow membership contract

Clarify that the key-list transport is a positive pruning hint, not universal Boolean equivalence, and test that a NULL probe survives the IS NULL wrapper.

Signed-off-by: discord9 <discord9@163.com>

* fix(query): retain safe bounds on membership fallback

When a key-list predicate cannot be shipped, keep top-level conjuncts that contain no membership and re-encode their conjunction. Drop membership-bearing conjuncts whole; fall back to true only when no safe bounds remain.

Signed-off-by: discord9 <discord9@163.com>

* fix(query): skip unrepresentable dynamic filter snapshots

Do not synthesize a true predicate when a runtime hash-table lookup has no wire representation. Capture and fanout skip the affected snapshot, keep the query running, and report PushedDown::No so the parent filter is retained. Safe membership-free conjuncts still travel, and a producer-supplied true remains encodable.

Cover restrictive-to-true updates, unsupported initial registrations with later supported updates, final unsupported cleanup, and NULL build keys.

Signed-off-by: discord9 <discord9@163.com>

* docs(query): clarify 642-key payload budget comment

Signed-off-by: discord9 <discord9@163.com>

* refactor(query): tighten dynamic membership encoding

Preserve static IN conjuncts when only a runtime lookup is unrepresentable, apply the IN-list exclusion only on the over-budget path, and derive the membership count guard from the byte budget before materializing literals. Document that decoded dynamic filters are pruning-only, fix the NOT/CASE contract wording, deduplicate test helpers, and justify the schema-less InList proto encoding.

Signed-off-by: discord9 <discord9@163.com>

* fix(query): reset stale dynamic filters on unsupported updates

Publish an explicit true reset when a producer generation cannot be carried, instead of leaving an older narrower predicate that could prune rows after widening. Keep the unsupported expression unrewritten; the reset is a transport-built invalidation. Type over-budget build-key memberships as DynFilterPayloadTooLarge, accept scalar-subquery IN lists under the conjunct-retention contract, and clarify the no-initial-predicate wording.

Signed-off-by: discord9 <discord9@163.com>

* docs(query): scope no-true rule to the encoder

Clarify that the encoder never substitutes true for a producer node, while the long-lived fanout path publishes an explicit true reset to invalidate a stale region predicate. Point at the fanout caller instead of claiming producers always skip refused generations.

Signed-off-by: discord9 <discord9@163.com>

* docs(query): correct reset wording

Fix the inverted conjunct-retention sentence and describe initial capture versus ongoing fanout resets consistently in the error docs.

Signed-off-by: discord9 <discord9@163.com>

---------

Signed-off-by: discord9 <discord9@163.com>
(cherry picked from commit f88af153ec)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* fix(auth): secure MySQL and PostgreSQL database selection (#10382)

* fix(mysql): authorize the default database at login

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(auth): validate MySQL handshake scope and SQL USE

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(auth): authorize PostgreSQL schema selection

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(servers): defer PostgreSQL database lookup until authorized

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* test(servers): propagate catalog lookup errors in protocol tests

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(postgres): skip unusable search path candidates

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(auth): log database authorization and selection outcomes

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(auth): preserve search path responses and schema boundaries

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(auth): clarify database selection diagnostics

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(mysql): format handshake username in structured logs

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(auth): secure database-less sessions and failed startup

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix(mysql): retain authorized default database selection

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* refactor(auth): separate database selection from request dispatch

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* test(mysql): align schema validation with database-less login

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* docs(auth): explain database-less session restrictions

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

---------

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
(cherry picked from commit 6e025eef61)
Signed-off-by: WenyXu <wenymedia@gmail.com>

* chore: bump candidate release version to v1.3.1

Signed-off-by: WenyXu <wenymedia@gmail.com>

---------

Signed-off-by: jeremyhi <fengjiachun@gmail.com>
Signed-off-by: WenyXu <wenymedia@gmail.com>
Signed-off-by: fys <fengys1996@gmail.com>
Signed-off-by: Lei, HUANG <ratuthomm@gmail.com>
Signed-off-by: lczllx <2181719471@qq.com>
Signed-off-by: Yvan Wang <131545713+BootstrapperSBL@users.noreply.github.com>
Signed-off-by: Byte-Naut <Byte-Naut@proton.me>
Signed-off-by: shuiyisong <xixing.sys@gmail.com>
Signed-off-by: Minh Vu <vuhoangminh97@gmail.com>
Signed-off-by: Floris Smit <florissmit10@gmail.com>
Signed-off-by: Ning Sun <sunning@greptime.com>
Signed-off-by: luofucong <luofc@foxmail.com>
Signed-off-by: luckys <190197589+luckysxx@users.noreply.github.com>
Signed-off-by: evenyag <realevenyag@gmail.com>
Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
Signed-off-by: Arpit Jain <arpitjain099@gmail.com>
Signed-off-by: discord9 <discord9@163.com>
Co-authored-by: jeremyhi <jiachun_feng@proton.me>
Co-authored-by: fys <40801205+fengys1996@users.noreply.github.com>
Co-authored-by: Lei, HUANG <6406592+v0y4g3r@users.noreply.github.com>
Co-authored-by: Dragon Roar <2181719471@qq.com>
Co-authored-by: Yvan Wang <131545713+BootstrapperSBL@users.noreply.github.com>
Co-authored-by: B. Naut <Byte-Naut@proton.me>
Co-authored-by: shuiyisong <113876041+shuiyisong@users.noreply.github.com>
Co-authored-by: Minh Vu <vuhoangminh97@gmail.com>
Co-authored-by: Floris Smit <florissmit10@users.noreply.github.com>
Co-authored-by: Ning Sun <sunng@protonmail.com>
Co-authored-by: LFC <990479+MichaelScofield@users.noreply.github.com>
Co-authored-by: luckysxx <2824602789@qq.com>
Co-authored-by: luckys <190197589+luckysxx@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: dennis zhuang <killme2008@gmail.com>
Co-authored-by: Yingwen <realevenyag@gmail.com>
Co-authored-by: Arpit Jain <3242828+arpitjain099@users.noreply.github.com>
Co-authored-by: sun <sunchang_long@163.com>
Co-authored-by: discord9 <discord9@163.com>
2026-11-13 20:03:13 +08:00
..
2023-09-28 08:08:43 +00:00
2025-07-04 09:52:47 +00:00