这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战
要想理解递归,除了抽象出同样的行为外,再就是找到循环的出口。
在上一次梳理的结果中可以看到,Edge1是递归的最后一个edge,在edge中,由于SourceOp的inputs为空,所以在edge中的出口是pipe3,也就是op.CacheMap:
f.NewFuncRequest(func(ctx context.Context) (interface{}, error) {
cm, err := e.op.CacheMap(ctx, index)
return cm, errors.Wrap(err, "failed to load cache key")
})
那现在是时候看一看这个自定义函数做了哪些处理,这样就是能调用pipe.OnSendCompletion来完成并退出这个操作,也就是递归结束开始的信号。
e.op.CacheMap究竟做了什么呢?
经过梳理,袁小白终于找到了CacheMap真正干活的那个了:
- 出发点-edge unpark,在这里将op.CacheMap封装成自定义函数,用于新建pipe
- op来自于jobs里用newSharedOp创建出来的sharedOp,这样可以将vertex封闭成在上一阶段处理所需要的状态和信息
- 在sharedOp的CacheMap里,是通过s.getOp,也就是sharedOp.getOp方法来获取op的
- 在上一步中,又是用的sharedOp的resolver来解析的,而这个resolver的类型是state.opts.ResolveOpFunc,它来自于jl.opts
- jl指的还是jobs,是在NewSolver创建方法被调用的时候,传入的SolverOpt来帮助说明的
- NewSolver是在solver的New方法里被调用的
- 实际的解析是发生在worker里的ResolveOp方法,在咱们的例子中,因为最后一个op是source op,对应在dockerfile里的指令就是
FROM FOO - 最终,是由我们ops.NewSourceOp方法创建出来的SourceOp实例,调用自己的CacheMap来真正处理这一请求的
梳理完后,袁小白不禁松了口气。 这个传递链也太长了,好多前面命名是用这个,后面是那个;要么就是从前到后用的都一个,一不留神就断掉了,难怪源码看起来这么费劲费时间。
好在有龙飞的全流程图,来对比着检验一下:
关键的点都有,还有一些细节看来可以补充下。
那再看看好容易找到的最终处理代码:
func (s *sourceOp) CacheMap(ctx context.Context, g session.Group, index int) (*solver.CacheMap, bool, error) {
src, err := s.instance(ctx)
if err != nil {
return nil, false, err
}
k, pin, cacheOpts, done, err := src.CacheKey(ctx, g, index)
if err != nil {
return nil, false, err
}
dgst := digest.FromBytes([]byte(sourceCacheType + ":" + k))
if strings.HasPrefix(k, "session:") {
dgst = digest.Digest("random:" + strings.TrimPrefix(dgst.String(), dgst.Algorithm().String()+":"))
}
var buildInfo map[string]string
if !strings.HasPrefix(s.op.Source.GetIdentifier(), "local://") {
buildInfo = map[string]string{s.op.Source.GetIdentifier(): pin}
}
return &solver.CacheMap{
// TODO: add os/arch
Digest: dgst,
Opts: cacheOpts,
BuildInfo: buildInfo,
}, done, nil
}
看来先是初始化一个sourceOp的实例 - s.instance(ctx),再根据缓存健从缓存中取对应的信息 - src.CacheKey(ctx, g, index)。
没忍住点进instance方法看了一下,发现这个实例,指的是实现了source.SourceInstance接口的对象,由s.sm.Resolve方法进行的解析,sm指的是source.Manager,Resolve方法也就是从自己的sources源中取出实例,可另一个问题又冒了出来 - 这些sources又是在哪里被装载的呢?