763f705ac1
flush_views_to_postmeta() adds warm-zone views back with a direct UPDATE, which the Hook Bus never sees: under dual_write/shadow_read the hp_listing_core.hp_view_count column drifted after every hourly batch and the (Redis) object cache kept the old count. After the UPDATE, drop the post's meta cache and, while native is still the source of truth, rebuild the entity's flat row with migrate_entity().
188 lines
8.1 KiB
Markdown
188 lines
8.1 KiB
Markdown
# Changelog
|
||
|
||
All notable changes to this AddOn will be documented here.
|
||
|
||
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||
Versioning follows [Semantic Versioning](https://semver.org/).
|
||
|
||
---
|
||
|
||
## [Unreleased]
|
||
|
||
---
|
||
|
||
## [0.5.3] — 2026-10-07 — 瀏覽數 flush 不再造成漂移
|
||
|
||
### Fixed
|
||
|
||
- **`TMDO_Listing_Stats::flush_views_to_postmeta()` 寫完後同步扁平表與快取**:每小時把 warm zone 的瀏覽數
|
||
用原子 `UPDATE … meta_value + N` 加回 `hp_view_count`,繞過了 meta API:Hook Bus 看不到這次寫入,
|
||
dual_write/shadow_read 下 `hp_listing_core.hp_view_count` 每批落後一次(夜間 drift-check 每晚 diff),
|
||
物件快取(Redis 下跨請求)也一直是舊值。現在 UPDATE 之後清掉該篇的 `post_meta` 快取,
|
||
並在原生仍是真相時(同時寫原生與扁平)用 `TMDO_Entity_Migration_Engine::migrate_entity()` 重建那一列。
|
||
`hp_view_count` 同時在 `hp_listing_core` 與 `hp_request_core`:只重建含這個 key、而且適用該篇 post type 的群組
|
||
(不帶 post type 的 `get_field()` 只回最後登記的那組)。aeav_only 不動(扁平才是真相)。
|
||
|
||
---
|
||
|
||
## [0.5.2] — 2026-10-05 — attribute bridge 的 meta key
|
||
|
||
### Fixed
|
||
|
||
- **`TMDO_HivePress_Attribute_Bridge` 組出正確的 meta key**:HivePress 把每個模型的屬性存成
|
||
`hp_{slug}`(例如 `hp_warranty`),不是 `hp_{model}_{slug}`。舊的 key 在資料庫裡不存在,
|
||
照它建議的欄位登記後永遠是空的。
|
||
|
||
---
|
||
|
||
## [0.5.1] — 2026-10-02 — 重新打包
|
||
|
||
### Changed
|
||
- 重新打包(無功能變更):與 WP-MVB v8 樞紐站(dev39)一同驗收後發版;程式碼與前一版相同。
|
||
|
||
---
|
||
|
||
## [0.5.0] — 2026-09-28
|
||
|
||
Released with 2meet-data-optimizer 1.4.0 (hardening 2.4).
|
||
|
||
### Changed
|
||
|
||
- `hp_listing_core` declares `(hp_price, post_id)`, `(hp_rating, post_id)` and
|
||
`(hp_hourly_rate, post_id)` indexes for the listing sorts HivePress offers:
|
||
with core 1.4.0 reading the ID tie-break from the flat `post_id`, a sort walks
|
||
the index and stops at the page instead of sorting every match (dev36, 1.3M
|
||
listings: price sort 14 s native → 0.37 s routed, hourly rate 11.5 s → 0.35 s,
|
||
rating 43 s → 0.51 s). Core 1.3.0 ignores indexes ending with `post_id`, so
|
||
nothing changes there. The pinned schema hash is unchanged. (PR-119)
|
||
- Transient entries in a persistent object cache live a day (`MAX_CACHE_TTL`,
|
||
HivePress's own default) instead of a week; a timeout still outlives its value
|
||
by a minute. At a week dev36 kept 4.7M entries (1 GB) in the shared Redis and
|
||
pushed other sites' keys out. Entries already cached keep their week. (PR-120)
|
||
|
||
---
|
||
|
||
## [0.4.0] — 2026-09-27
|
||
|
||
Released with 2meet-data-optimizer 1.3.0 (hardening 2.3).
|
||
|
||
### Changed
|
||
|
||
- `hp_listing_core` declares indexes on `hp_hourly_rate` and `hp_rating_count`
|
||
(HivePress filters and sorts on both). They are declared through the group's
|
||
`indexes`, not `searchable`, so the pinned schema hash is unchanged and the
|
||
table is not rebuilt; core 1.3.0 adds them in place from WP-CLI / WP-Cron.
|
||
dev36 (902k listings): two indexes in about 4.7 s; routed `rating_count IN`
|
||
2.1 s → 0.36 s, `rating_count !=` 2.1 s → 0.76 s, hourly rate + price sort
|
||
2.1 s → 0.65 s. (PR-090)
|
||
- With a persistent object cache, transient entries now expire: a value after
|
||
a week (`MAX_CACHE_TTL`; HivePress writes it before its timeout, and a miss
|
||
only makes it rebuild), its timeout a minute after the later of that and its
|
||
own time — HivePress reads a missing timeout as "never expires", so the
|
||
timeout must go last. Entries of deleted posts no longer pile up. (PR-099,
|
||
PR-115)
|
||
|
||
### Added
|
||
|
||
- Opt-in flattening of a site's own listing attributes: the
|
||
`tmdo_hp_listing_attribute_fields` filter (`'hp_warranty' => 'integer'`)
|
||
registers them in an `hp_listing_attrs` group — a new table, each column
|
||
indexed at creation — leaving `hp_listing_core` unchanged. Types: text,
|
||
textarea, integer, decimal, boolean, date, datetime, timestamp, enum. The
|
||
group is registered once themes load, so the filter may live in a theme;
|
||
core 1.3.0 keeps it native until a drift fix has filled it. (PR-109, PR-115)
|
||
|
||
### Upgrading
|
||
|
||
- Deploy with core 1.3.0: on an older core the AddOn declares no index at all
|
||
(core 1.2.0 would run the ALTER inside a web request). (PR-090b)
|
||
- Redis: entries written by 0.3.0 have no TTL (dev36 had 3.0 M). Give them one
|
||
with non-blocking commands — never `wp cache flush-group`, which the Redis
|
||
Object Cache drop-in runs as one server-side script and which blocked Redis
|
||
on a large group. `NX` leaves entries written by 0.4.0 alone:
|
||
|
||
```
|
||
redis-cli --scan --pattern '<WP_CACHE_KEY_SALT>wp:tmdo_hp_transient:*' \
|
||
| awk '{printf "EXPIRE %s 604800 NX\r\n", $0}' | redis-cli --pipe
|
||
redis-cli --scan --pattern '<WP_CACHE_KEY_SALT>wp:tmdo_hp_transient:_transient_timeout_*' \
|
||
| awk '{printf "EXPIRE %s 608400 GT\r\n", $0}' | redis-cli --pipe
|
||
```
|
||
|
||
The second pass makes timeouts outlive their values. dev36: 3,013,352 keys
|
||
in 45 s and 1,867,569 timeouts in 32 s, 0 errors, Redis latency max 2 ms. The pattern
|
||
follows the drop-in's key layout (`<salt><blog prefix>:<group>:<key>`);
|
||
check it with `redis-cli --scan --pattern '<salt>*tmdo_hp_transient*' | head`
|
||
first. redis-cli 7 has no `--count` with `--scan`.
|
||
|
||
---
|
||
|
||
## [0.3.0] — 2026-09-27
|
||
|
||
Released with 2meet-data-optimizer 1.2.0 (hardening 2.2).
|
||
|
||
### Changed
|
||
|
||
- With a persistent object cache (`wp_using_ext_object_cache()`), the
|
||
transient filter keeps HivePress's per-post transient pairs in the
|
||
`tmdo_hp_transient` cache group instead of `wp_options`, where they piled
|
||
up (1.15 M pairs on dev36). The cache is then the only store: rows written
|
||
before are neither read nor deleted (HivePress rebuilds those caches once),
|
||
because a `get_option()` miss adds the name to WordPress's `notoptions` for
|
||
good and a hit keeps the old row in the `options` cache group with no expiry.
|
||
Without an object cache nothing changes. After upgrading, remove the rows
|
||
left behind with core's `wp tmdo cleanup-hp-transients --options --confirm`.
|
||
|
||
### Fixed
|
||
|
||
- The detected / bound status cells of the HivePress admin table go through
|
||
`esc_html()` (2.1 audit); output unchanged.
|
||
|
||
### Docs
|
||
|
||
- `docs/REGISTERED-KEYS.md`: the six registered keys HivePress never writes
|
||
as meta, because its models alias them to core columns (listing
|
||
`hp_vendor` → `post_parent`, `hp_status` → `post_status`; request
|
||
`hp_status`, `hp_user`; vendor `hp_user`; review `hp_rating` →
|
||
`comment_karma`). The schema stays; their flat columns are always NULL.
|
||
- `docs/HIVEPRESS-COVERAGE.md`: dev36 inventory of the meta each HivePress
|
||
post type holds, where the extensions keep their data and the query shapes
|
||
the front end produces. Attribute auto-registration is not needed now:
|
||
every meta key HivePress queries is registered.
|
||
|
||
---
|
||
|
||
## [0.2.0] — 2026-09-26
|
||
|
||
Released with 2meet-data-optimizer 1.1.0 (hardening 2.1, search-index mode).
|
||
|
||
### Changed
|
||
|
||
- HivePress model query / search hooks (`optimize_query`, `optimize_search`)
|
||
are no longer bound while core's post router is active
|
||
(`TMDO_Post_Query_Router::is_router_active()`): core routes HivePress
|
||
searches, including `_sort`, to the entity flat tables, and two routers
|
||
would claim the same clauses.
|
||
|
||
### Added
|
||
|
||
- `tests/integration/`:bootstrap(複用核心的 integration bootstrap,連真實
|
||
MariaDB)+ `phpunit-integration.xml` + CI job,移植 5 個測試檔
|
||
(`HivepressIntegrationTest`、`HivepressTermCommentFieldsTest`、
|
||
`HivepressTransientFilterTest`、`ListingMetaInterceptorTest`、
|
||
`TermCommentBackfillTest`),共 57 tests。
|
||
- `tests/unit/ListingStatsTest.php`(9 tests)。unit bootstrap 先 define
|
||
`TMDO_TEST_SKIP_LISTING_STATS_STUB`,核心 bootstrap 便會跳過它的
|
||
`TMDO_Listing_Stats` stub,改載入本 AddOn 的真實實作。145 → 154 tests。
|
||
|
||
- Phase 3 implementation in progress.
|
||
|
||
---
|
||
|
||
## [0.1.0] — TBD
|
||
|
||
### Added (initial release, scaffold)
|
||
|
||
- Plugin scaffold from `wp-data-optimizer v2.16.0` extraction plan.
|
||
- Plugin Header + bootstrap stub + 7 MD + composer.json.
|
||
- Requires Plugins: `2meet-data-optimizer`, `hivepress`.
|