mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-11-07 09:47:23 +00:00
fix(plugins): 分片 VM 拿不到插件配置
页面探测走的是 fanout(分片)路径,结果写回走共享 VM。分片 VM 从同一份 源码启动,但没有 state 文件、没有插件身份,所以 PLUGIN.prices(配置,不是 累计状态)在分片里是空的。 对只读累计状态的插件无所谓;对按配置分支的插件就是致命的。OpenDesign 适配器读 prices.control_key,未播种时它对每个主机都得出「未配置」并报 skip,而同一个调用走共享 VM 是好的。也就是说:生产上死、所有调共享 VM 的 测试全绿。 pool 在每次取用时推送配置(不只是首次启动时),并把配置变化也带给池中 复用的 VM;推送是拷贝,分片拿不到共享表的引用。 这条不是在写代码时想到的,是端到端验证时撞出来的:单元测试全过,隔离实例 打真上游时 vela 报「未配置」,而 control_key 明明写进去了。
This commit is contained in:
@ -1172,6 +1172,54 @@ func audienceAllows(want, role string) bool {
|
||||
// upgrade would overwrite anyway.
|
||||
type visibilityOverrides map[string]string
|
||||
|
||||
// seedShardConfig copies the shared plugin's `prices` table onto a shard VM.
|
||||
//
|
||||
// `prices` is CONFIGURATION, as opposed to `state` (accumulated history). A shard
|
||||
// must not write state — its copy is thrown away — but it must be able to READ
|
||||
// the operator's configuration, or a plugin that branches on its configuration
|
||||
// behaves one way through Plugins.Call and another through ShardCall.
|
||||
//
|
||||
// Found by the OpenDesign adapter, which reads prices.control_key: without this, a
|
||||
// fanout probe saw an empty table, concluded "not configured", and reported a
|
||||
// skip for every host — while the identical call through the shared VM worked.
|
||||
// The page uses the fanout path, so the feature would have been dead in
|
||||
// production and alive in every test that called the shared VM.
|
||||
//
|
||||
// Called on every checkout (boot and reuse), so a configuration change reaches
|
||||
// pooled VMs too. The push is a copy: shards get the same values, never a live
|
||||
// reference to the shared table.
|
||||
func (ps *Plugins) seedShardConfig(name string, w *worker) {
|
||||
if ps == nil || w == nil || w.L == nil {
|
||||
return
|
||||
}
|
||||
ps.mu.RLock()
|
||||
p := ps.find(name)
|
||||
ps.mu.RUnlock()
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
p.mu.Lock()
|
||||
if p.state == nil || p.state.L == nil || p.state.L == w.L {
|
||||
p.mu.Unlock()
|
||||
return
|
||||
}
|
||||
prices := snapshotField(p.state.L, "prices")
|
||||
p.mu.Unlock()
|
||||
if prices == nil {
|
||||
return
|
||||
}
|
||||
L := w.L
|
||||
L.SetTop(0)
|
||||
defer L.SetTop(0)
|
||||
L.GetGlobal(pluginGlobal)
|
||||
if L.IsNil(-1) {
|
||||
return
|
||||
}
|
||||
plug := L.GetTop()
|
||||
pushGoValue(L, prices)
|
||||
L.SetField(plug, "prices")
|
||||
}
|
||||
|
||||
// SetPluginVisibility records an operator override for a plugin's audience. An
|
||||
// empty value clears the override and restores the plugin's own default.
|
||||
func (ps *Plugins) SetPluginVisibility(plugin, audience string) {
|
||||
|
||||
Reference in New Issue
Block a user