Appearance
类型、容器与模块边界:先决定谁拥有数据
网关接受请求后会把候选端点拷贝出来,在锁外选路、发网络请求。如果只复制 slice 变量,多个 goroutine 可能改到同一底层数组。要理解为什么代码里出现 append 到空切片,先在本机观察它。
完整源码:modeldock/labs/runtime/contracts_test.go · 第 13–34 行
go
func (*lookupError) Error() string { return "lookup" }
func TestOwnership(t *testing.T) {
var p *lookupError
var err error = p
if err == nil {
t.Fatal("interface lost dynamic type")
}
page := make([]byte, 4, 64)
head := page[:2]
head = append(head, 9)
if page[2] != 9 {
t.Fatal("expected shared storage")
}
isolated := page[:2:2]
isolated = append(isolated, 7)
if page[2] != 9 || isolated[2] != 7 {
t.Fatal("unexpected alias")
}
}
func TestCancelReachesUpstream(t *testing.T) {sh
(cd modeldock && go test -v -run '^TestOwnership$' ./labs/runtime)
rg -n 'append\(\[\]string|sort.Slice' modeldock/operator/cmd/gateway/main.go三种常见表示,三种不同的错法
接口。 空接口记录动态类型和值;非空接口的运行时表示包含与具体类型匹配的方法表,间接分派到具体方法。实现可去虚化,因此“所有接口调用都分配堆对象”不成立。带类型 nil 指针赋给 error 后动态类型仍在,因此 err != nil。在返回成功时返回真正的 nil;在使用方定义小接口,Operator 注入 client.Client,测试使用 API Server,不用仿造整个平台。
切片。 header 描述指针、len、cap;append 在容量够时原地写,否则申请新数组复制旧元素。增长系数与分配粒度由实现决定,不能按“永远翻倍”做业务判断。full slice expression 限制后续 append 可复用的容量,但不会隔离已存在元素的修改。网络 worker 要独立快照就 clone/复制;长期缓存小子片时可能把整个大数组留活。
Map。 Go 1.26 的实现可在 GOROOT/src/internal/runtime/maps 跟读,不能继续用早期固定桶结构解释新版本:哈希选表/组,通过控制字节缩小候选,再比较键;表增长可能分裂,迭代顺序不能作为协议。并发读且没有写入可行;并发写必须同步。sync.Map.LoadOrStore 适合分别按键建缓存,不能维护跨键的额度不变量。
sh
rg -n 'type (Map|table|groupReference) struct' "$(go env GOROOT)/src/internal/runtime/maps"
rg -n 'func growslice' "$(go env GOROOT)/src/runtime/slice.go"
(cd modeldock && go test -gcflags=-m ./labs/runtime 2>&1 | grep 'escapes to heap' | head)错误与资源释放如何形成接口契约
在数据层用 fmt.Errorf("reserve: %w", err) 保留上层可判断的 cause;HTTP 层用 errors.Is/As 区分取消、超时、冲突,不把所有错误转成 500。defer rows.Close()/resp.Body.Close() 要在成功取得资源后登记;长循环中 defer 堆到函数返回会占满池,宜独立成一次调用。recover 只能在同 goroutine 的 defer 中生效,不能当业务重试。轻量模拟、数据库、控制器各自有 go.mod;在顶层运行一次 go test ./... 不会跨进嵌套模块。下一步把所有权规则带进通道和锁。
为什么复制一个 slice 后还要加锁?
复制 header 并不复制底层数组。网关若在锁内把 routes[model] 赋给一个局部变量,解锁后直接对这个局部切片排序,仍可能重排其他请求可见的候选表。检查项目里的 append 到空切片:锁内复制端点元素,再在自己的快照上选择;网络调用在锁外。若元素从字符串改为带可变字段的指针,复制元素也只是复制地址,需要重新决定字段的所有权。验证别名的实验可以用来检查这一推理的前半段。