fix(ncm): 缓存命中改单请求出参,移除全局命中计数(评审 P2-2/P2-1)
问题(P2-2):离线判定用 `ncm_apicache_hits()` 前后差值判「本次是否缓存命中」—— ①其它 worker 的命中会污染差值 → 误判「已回在线」;②盘缓存分支的 g_hits++ 在 g_mu 锁外, 跨线程自增/64 位读存在竞争与撕裂可能。 修法(出参方案): - `api_post_cached(..., int *from_cache)`:命中置 1(未走网络),miss/失败为 0; 四个调用点显式传参。 - 新增 `ncm_api_user_playlist_ex(..., int *out_from_cache)`;out_n 与原接口一致**必填** (NULL→NCM_ERR_ARG,修入口容忍 NULL、成功路径却无条件解引用的契约不一致,评审 P2-1); out_from_cache 可 NULL。旧 `ncm_api_user_playlist` 为薄包装(调用方零改动)。 - `appstate_load_playlists_ex` 透传该标志;home_worker 用本次请求的 from_cache 判定是否 回在线;歌单页只在失败时同步离线态(成功可能是缓存命中,不据此清离线)。 - 删除 `ncm_apicache_hits()`/g_hits(无使用者,同时消除数据竞争);盘缓存命中分支两行 合并为一行(行为不变,重构)。 - 测试:session_gate_sim 新增 D2 五例(miss→0 且真网络 +1 次 / 命中→1 且 0 请求 / 失败→0 / 兼容入口不崩 / out_n 必填契约);playlist_race_sim、plpage_sim 补 `_ex` 桩。 验证:session_gate_sim 88 PASS/0 FAIL;test_ncm_core_host 139 PASS/0 FAIL; http_truncate_sim 13 PASS/0 FAIL;核心 13 TU(-Wall -Wextra)0 warning。
This commit is contained in:
1 parent
b1d607d43f
commit
31879f3302
12 files changed
+134
-49
No files matched your search
@@ -174,9 +174,14 @@ static void api_cache_key(const char *path, const char *json, char *out, size_t
|
||||
}
|
||||
|
||||
/* 带缓存的 POST:先查内存/盘缓存(TTL 内命中则直接解析回填);miss/过期/手动刷新 → 真网络,
|
||||
* 成功且非手动刷新 → 写缓存。 */
|
||||
* 成功且非手动刷新 → 写缓存。
|
||||
* from_cache(可 NULL):1=本次结果来自接口缓存(**没有**真正走网络),0=走了网络/失败。
|
||||
* 离线判定用它——缓存命中返回 NCM_OK 不代表网络已恢复(M9 评审 P2-2:不能用全局计数
|
||||
* 前后差值判定,其它 worker 的命中会污染,且计数自增存在跨线程竞争)。 */
|
||||
static int api_post_cached(const char *device_id, const char *cookie, const char *path,
|
||||
const char *json, int kind, size_t resp_cap, api_json_t *c) {
|
||||
const char *json, int kind, size_t resp_cap, api_json_t *c,
|
||||
int *from_cache) {
|
||||
if (from_cache) *from_cache = 0;
|
||||
int refresh = api_refresh_match(path);
|
||||
char key[NCM_APICACHE_KEY_MAX];
|
||||
api_cache_key(path, json, key, sizeof key);
|
||||
@@ -186,6 +191,7 @@ static int api_post_cached(const char *device_id, const char *cookie, const char
|
||||
if (buf) {
|
||||
size_t len = 0;
|
||||
if (ncm_apicache_get(key, buf, resp_cap, &len) == NCM_OK) {
|
||||
if (from_cache) *from_cache = 1;
|
||||
c->rbuf = buf;
|
||||
c->resp.body = buf; /* 缓存的 body 从缓冲头开始 */
|
||||
c->resp.body_len = len;
|
||||
@@ -300,10 +306,13 @@ static int parse_songs_array(const ncm_json_t *j, int arr, ncm_song_t *out, int
|
||||
return NCM_OK;
|
||||
}
|
||||
|
||||
int ncm_api_user_playlist(const char *device_id, const char *cookie, const char *uid,
|
||||
int limit, int offset, ncm_playlist_t *out, int max, int *out_n) {
|
||||
int ncm_api_user_playlist_ex(const char *device_id, const char *cookie, const char *uid,
|
||||
int limit, int offset, ncm_playlist_t *out, int max, int *out_n,
|
||||
int *out_from_cache) {
|
||||
/* 与原接口契约一致:out_n 必填(成功路径无条件回填);out_from_cache 可 NULL。 */
|
||||
if (!out_n) return NCM_ERR_ARG;
|
||||
*out_n = 0;
|
||||
if (out_from_cache) *out_from_cache = 0;
|
||||
if (!uid) return NCM_ERR_ARG;
|
||||
char json[256];
|
||||
snprintf(json, sizeof json, "{\"uid\":\"%s\",\"limit\":%d,\"offset\":%d,\"includeVideo\":true}",
|
||||
@@ -314,7 +323,7 @@ int ncm_api_user_playlist(const char *device_id, const char *cookie, const char
|
||||
* (日志 home: playlists rc=-11)。该响应不含 tracks(每首 tracks:null),1MB 足够,
|
||||
* 用 NCM_RESP_MAX(与 vip/info 同口径的堆瞬态,解析后即释,非 bss)。 */
|
||||
int rc = api_post_cached(device_id, cookie, "/api/user/playlist", json, NCM_CRYPT_WEAPI,
|
||||
NCM_RESP_MAX, &c);
|
||||
NCM_RESP_MAX, &c, out_from_cache);
|
||||
if (rc != NCM_OK) return rc;
|
||||
|
||||
int arr = ncm_json_path(&c.j, "playlist");
|
||||
@@ -337,6 +346,12 @@ int ncm_api_user_playlist(const char *device_id, const char *cookie, const char
|
||||
return NCM_OK;
|
||||
}
|
||||
|
||||
/* 兼容入口(不关心是否缓存命中):等价 ncm_api_user_playlist_ex(..., NULL)。 */
|
||||
int ncm_api_user_playlist(const char *device_id, const char *cookie, const char *uid,
|
||||
int limit, int offset, ncm_playlist_t *out, int max, int *out_n) {
|
||||
return ncm_api_user_playlist_ex(device_id, cookie, uid, limit, offset, out, max, out_n, NULL);
|
||||
}
|
||||
|
||||
int ncm_api_playlist_detail(const char *device_id, const char *cookie, const char *playlist_id,
|
||||
char *name, size_t name_sz, char *cover, size_t cover_sz,
|
||||
int64_t *ids, int id_cap, int *out_id_n) {
|
||||
@@ -350,7 +365,7 @@ int ncm_api_playlist_detail(const char *device_id, const char *cookie, const cha
|
||||
|
||||
api_json_t c; memset(&c, 0, sizeof c);
|
||||
int rc = api_post_cached(device_id, cookie, "/api/v6/playlist/detail", json, NCM_CRYPT_EAPI,
|
||||
2 * 1024 * 1024, &c);
|
||||
2 * 1024 * 1024, &c, NULL);
|
||||
if (rc != NCM_OK) return rc;
|
||||
|
||||
int pl = ncm_json_path(&c.j, "playlist");
|
||||
@@ -394,7 +409,7 @@ int ncm_api_song_detail(const char *device_id, const char *cookie, const int64_t
|
||||
|
||||
api_json_t c; memset(&c, 0, sizeof c);
|
||||
int rc = api_post_cached(device_id, cookie, "/api/v3/song/detail", json, NCM_CRYPT_EAPI,
|
||||
RESP_MED, &c);
|
||||
RESP_MED, &c, NULL);
|
||||
if (rc != NCM_OK) return rc;
|
||||
|
||||
int arr = ncm_json_path(&c.j, "songs");
|
||||
@@ -677,7 +692,7 @@ static int cloudsearch_post(const char *device_id, const char *cookie, const cha
|
||||
snprintf(json, sizeof json, "{\"s\":\"%s\",\"type\":%d,\"offset\":%d,\"limit\":%d,\"total\":true}",
|
||||
esc, type, offset > 0 ? offset : 0, limit > 0 ? limit : 30);
|
||||
return api_post_cached(device_id, cookie, "/api/cloudsearch/pc", json, NCM_CRYPT_WEAPI,
|
||||
RESP_MED, c);
|
||||
RESP_MED, c, NULL);
|
||||
}
|
||||
|
||||
/* 解析一处「歌单对象数组」(cloudsearch type=1000)。id 可为数字/字符串(均文本拷贝)。 */
|
||||
|
||||
@@ -89,7 +89,13 @@ typedef struct ncm_play_url {
|
||||
} ncm_play_url_t;
|
||||
|
||||
/* 用户歌单列表(weapi /user/playlist)。uid 必填;limit/offset 翻页。
|
||||
* 返回 NCM_OK;songs/out_n 为列表。 */
|
||||
* 返回 NCM_OK;songs/out_n 为列表。
|
||||
* _ex 变体额外回填 *out_from_cache(可 NULL):1=本次结果来自接口缓存(未走网络),
|
||||
* 0=走了网络或失败。离线判定用——缓存命中返回 NCM_OK 不代表网络已恢复(M9)。
|
||||
* 契约与原接口一致:out_n 必填(NULL → NCM_ERR_ARG)。 */
|
||||
int ncm_api_user_playlist_ex(const char *device_id, const char *cookie, const char *uid,
|
||||
int limit, int offset, ncm_playlist_t *out, int max, int *out_n,
|
||||
int *out_from_cache);
|
||||
int ncm_api_user_playlist(const char *device_id, const char *cookie, const char *uid,
|
||||
int limit, int offset, ncm_playlist_t *out, int max, int *out_n);
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@ static char g_disk_dir[DISK_DIR_MAX];
|
||||
static int g_disk_on = 0;
|
||||
static int g_ttl_s = NCM_APICACHE_TTL_S; /* 默认 120s */
|
||||
static int64_t g_last_purge_ms = 0; /* 盘过期清理节流(单调钟)*/
|
||||
static uint64_t g_hits = 0; /* 命中计数(M9 离线判定用,仅单调递增) */
|
||||
|
||||
static int64_t clock_ms(clockid_t id) {
|
||||
struct timespec ts;
|
||||
@@ -214,7 +213,6 @@ int ncm_apicache_get(const char *key, char *buf, size_t buf_sz, size_t *len)
|
||||
memcpy(buf, g_e[idx].body, n);
|
||||
g_e[idx].last_ms = now; /* LRU touch */
|
||||
if (len) *len = n;
|
||||
g_hits++;
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
return NCM_OK;
|
||||
}
|
||||
@@ -224,23 +222,13 @@ int ncm_apicache_get(const char *key, char *buf, size_t buf_sz, size_t *len)
|
||||
/* 内存 miss → 盘缓存兜底(命中回填内存)。 */
|
||||
if (g_disk_on) {
|
||||
int rc = disk_get(key, buf, buf_sz, len);
|
||||
if (rc == NCM_OK) {
|
||||
if (len) ncm_apicache_put(key, buf, *len);
|
||||
g_hits++;
|
||||
}
|
||||
/* 合并为一行,行为不变(重构):len 为空时不回填内存。 */
|
||||
if (rc == NCM_OK && len) ncm_apicache_put(key, buf, *len);
|
||||
return rc;
|
||||
}
|
||||
return NCM_ERR_NOENT;
|
||||
}
|
||||
|
||||
uint64_t ncm_apicache_hits(void)
|
||||
{
|
||||
pthread_mutex_lock(&g_mu);
|
||||
uint64_t v = g_hits;
|
||||
pthread_mutex_unlock(&g_mu);
|
||||
return v;
|
||||
}
|
||||
|
||||
void ncm_apicache_put(const char *key, const char *body, size_t len)
|
||||
{
|
||||
if (!key || !body || len == 0) return;
|
||||
|
||||
@@ -55,10 +55,6 @@ void ncm_apicache_ttl_set(int seconds);
|
||||
/* 当前内存条目数(诊断/测试)。 */
|
||||
int ncm_apicache_mem_count(void);
|
||||
|
||||
/* 命中计数(单调递增;M9 离线判定用:App 据此区分「真·网络成功」与「离线时读到旧缓存」,
|
||||
* 避免离线提示/账号态被缓存命中误判为「已回在线」)。 */
|
||||
uint64_t ncm_apicache_hits(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in new issue
Block a user