这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战
从龙飞给出的buildctl build全景时序图中可以了解到:
Worker是用来真正运行容器的,那jobs的定位又是什么呢?
带着这个问题,袁小白打开了j.list.load(e.Vertex, nil, j),稍早之前有看到初始化Job的时候,j.list指的是jobs.go中的Solver,所以j.list.load就是:
func (jl *Solver) load(v, parent Vertex, j *Job) (Vertex, error) {
jl.mu.Lock()
defer jl.mu.Unlock()
cache := map[Vertex]Vertex{}
return jl.loadUnlocked(v, parent, j, cache)
}
这里传入的的参数有v, parent都是Vertex,其中v就是edge.Vertex,从函数的定义来看,传入了vertex,返回的也是vertex,看起来像是对vertext做了一些处理,代码中实际调用的是jl.loadUnlocked:
func (jl *Solver) loadUnlocked(v, parent Vertex, j *Job, cache map[Vertex]Vertex) (Vertex, error) {
if v, ok := cache[v]; ok {
return v, nil
}
...
}
直接从传入的Cache map中取vertex,如果有直接返回,那我们可以看出这里的cache是指的正在处理的Edge中的Vertex。
func (jl *Solver) loadUnlocked(v, parent Vertex, j *Job, cache map[Vertex]Vertex) (Vertex, error) {
...
inputs := make([]Edge, len(v.Inputs()))
for i, e := range v.Inputs() {
v, err := jl.loadUnlocked(e.Vertex, parent, j, cache)
if err != nil {
return nil, err
}
inputs[i] = Edge{Index: e.Index, Vertex: v}
}
...
}
这里又出现了递归,而且是根据vertex的input,循环load处于unlock状态的vertex,值得注意的是这里用的parent是同一个,并不是上一级的Vertex。 可以看出每一级只关心自己的这一级的input。
func (jl *Solver) loadUnlocked(v, parent Vertex, j *Job, cache map[Vertex]Vertex) (Vertex, error) {
...
// if same vertex is already loaded without cache just use that
st, ok := jl.actives[dgstWithoutCache]
if !ok {
...
v = &vertexWithCacheOptions{
Vertex: v,
dgst: dgst,
inputs: inputs,
}
...
}
if !ok {
st = &state{
opts: jl.opts,
jobs: map[*Job]struct{}{},
parents: map[digest.Digest]struct{}{},
childVtx: map[digest.Digest]struct{}{},
allPw: map[progress.Writer]struct{}{},
mpw: progress.NewMultiWriter(progress.WithMetadata("vertex", dgst)),
mspan: tracing.NewMultiSpan(),
vtx: v,
clientVertex: initClientVertex(v),
edges: map[Index]*edge{},
index: jl.index,
mainCache: jl.opts.DefaultCache,
cache: map[string]CacheManager{},
solver: jl,
origDigest: origVtx.Digest(),
}
jl.actives[dgst] = st
}
...
}
这里的重点是将vertex转换成vertexWithCacheOptions,也就是适用于缓存的状态。 紧接着创建出了state,这里的state,从结构体数据项来看是综合了所有的job相关的信息。
总的来说,就是将所有的vertex转换成有cacheOptions的状态,并用dgst(vertext唯一标识)关联上对应的state。
再看Build:
func (j *Job) Build(ctx context.Context, e Edge) (CachedResult, BuildInfo, error) {
...
v, err := j.list.load(e.Vertex, nil, j)
...
e.Vertex = v
res, err := j.list.s.build(ctx, e)
...
return res, j.walkBuildInfo(ctx, e, make(BuildInfo)), nil
}
将vertex转换成有cacheOptions状态的vertext后,替换掉了原Vertex - e.Vertex = v。
最后用j.list.s.build进行构建。
j.list.s指的是scheduler调度器,看来每个job都有自己的一个高度器,而这个调度器需要有缓存信息的vertex进行构建。
袁小白感觉自己正准备进入buildctl build中的引擎部位,前面那么多的铺垫都是在为这一刻做准备,紧张又兴奋!