package ops2 import ( "fmt" "io" "time" "gitlink.org.cn/cloudream/common/pkgs/future" "gitlink.org.cn/cloudream/common/pkgs/logger" "gitlink.org.cn/cloudream/common/utils/io2" "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch/dag" "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch/exec" "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch2" "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/pool" stgtypes "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/types" jcstypes "gitlink.org.cn/cloudream/jcs-pub/common/types" ) const ( BaseReadStatsStoreKey = "Stats.BaseRead" ) func init() { exec.UseOp[*BaseWrite]() exec.UseOp[*BaseRead]() exec.UseOp[*BaseReadDyn]() exec.UseVarValue[*BaseReadStatsValue]() } type BaseReadStatsValue struct { Length int64 ElapsedTime time.Duration Location exec.Location } func (v *BaseReadStatsValue) Clone() exec.VarValue { return &BaseReadStatsValue{ Length: v.Length, ElapsedTime: v.ElapsedTime, Location: v.Location, } } type BaseRead struct { Output exec.VarID UserSpace jcstypes.UserSpaceDetail Path jcstypes.JPath Option stgtypes.OpenOption } func (o *BaseRead) Execute(ctx *exec.ExecContext, e *exec.Executor) error { logger. WithField("Output", o.Output). WithField("UserSpace", o.UserSpace). WithField("Path", o.Path). Debug("base read") defer logger.Debug("base read end") stgPool, err := exec.GetValueByType[*pool.Pool](ctx) if err != nil { return fmt.Errorf("getting storage pool: %w", err) } store, err := stgPool.GetBaseStore(&o.UserSpace) if err != nil { return fmt.Errorf("getting base store of storage %v: %w", o.UserSpace, err) } stream, err := store.Read(o.Path, o.Option) if err != nil { return fmt.Errorf("reading object %v: %w", o.Path, err) } startTime := time.Now() counter := io2.CounterCloser(stream, nil) fut := future.NewSetVoid() output := &exec.StreamValue{ Stream: io2.AfterReadClosed(counter, func(closer io.ReadCloser) { fut.SetVoid() }), } e.PutVar(o.Output, output) err = fut.Wait(ctx.Context) e.Store(BaseReadStatsStoreKey, &BaseReadStatsValue{ Length: counter.Count(), ElapsedTime: time.Since(startTime), Location: e.Location(), }) return err } func (o *BaseRead) String() string { return fmt.Sprintf("BaseRead(opt=%v) %v:%v -> %v", o.Option, o.UserSpace, o.Path, o.Output) } type BaseReadDyn struct { UserSpace jcstypes.UserSpaceDetail Output exec.VarID FileInfo exec.VarID Option stgtypes.OpenOption } func (o *BaseReadDyn) Execute(ctx *exec.ExecContext, e *exec.Executor) error { logger. WithField("Output", o.Output). WithField("UserSpace", o.UserSpace). WithField("Path", o.FileInfo). Debug("base read dynamic") defer logger.Debug("base read dynamic end") stgPool, err := exec.GetValueByType[*pool.Pool](ctx) if err != nil { return fmt.Errorf("getting storage pool: %w", err) } info, err := exec.BindVar[*FileInfoValue](e, ctx.Context, o.FileInfo) if err != nil { return err } store, err := stgPool.GetBaseStore(&o.UserSpace) if err != nil { return fmt.Errorf("getting base store of storage %v: %w", o.UserSpace, err) } stream, err := store.Read(info.Path, o.Option) if err != nil { logger.Warnf("reading file %v: %v", info.Path, err) return fmt.Errorf("reading object %v: %w", o.FileInfo, err) } startTime := time.Now() counter := io2.CounterCloser(stream, nil) fut := future.NewSetVoid() output := &exec.StreamValue{ Stream: io2.AfterReadClosed(counter, func(closer io.ReadCloser) { fut.SetVoid() }), } e.PutVar(o.Output, output) err = fut.Wait(ctx.Context) e.Store(BaseReadStatsStoreKey, &BaseReadStatsValue{ Length: counter.Count(), ElapsedTime: time.Since(startTime), Location: e.Location(), }) return err } func (o *BaseReadDyn) String() string { return fmt.Sprintf("BaseReadDyn(opt=%v) %v:%v -> %v", o.Option, o.UserSpace, o.FileInfo, o.Output) } type BaseWrite struct { Input exec.VarID UserSpace jcstypes.UserSpaceDetail Path jcstypes.JPath FileInfo exec.VarID Option stgtypes.WriteOption } func (o *BaseWrite) Execute(ctx *exec.ExecContext, e *exec.Executor) error { logger. WithField("Input", o.Input). Debugf("write file to base store") defer logger.Debugf("write file to base store finished") stgPool, err := exec.GetValueByType[*pool.Pool](ctx) if err != nil { return fmt.Errorf("getting storage pool: %w", err) } store, err := stgPool.GetBaseStore(&o.UserSpace) if err != nil { return fmt.Errorf("getting base store of storage %v: %w", o.UserSpace, err) } input, err := exec.BindVar[*exec.StreamValue](e, ctx.Context, o.Input) if err != nil { return err } defer input.Stream.Close() ret, err := store.Write(o.Path, input.Stream, o.Option) if err != nil { return err } e.PutVar(o.FileInfo, &FileInfoValue{ FileInfo: ret, }) return nil } func (o *BaseWrite) String() string { return fmt.Sprintf("BaseWrite %v -> %v:%v, %v", o.Input, o.UserSpace, o.Path, o.FileInfo) } type BaseReadNode struct { dag.NodeBase From ioswitch2.From UserSpace jcstypes.UserSpaceDetail Path jcstypes.JPath Option stgtypes.OpenOption } func (b *GraphNodeBuilder) NewBaseRead(from ioswitch2.From, userSpace jcstypes.UserSpaceDetail, path jcstypes.JPath, opt stgtypes.OpenOption) *BaseReadNode { node := &BaseReadNode{ From: from, UserSpace: userSpace, Path: path, Option: opt, } b.AddNode(node) node.OutputStreams().Init(node, 1) return node } func (t *BaseReadNode) GetFrom() ioswitch2.From { return t.From } func (t *BaseReadNode) Output() dag.StreamOutputSlot { return dag.StreamOutputSlot{ Node: t, Index: 0, } } func (t *BaseReadNode) GenerateOp() (exec.Op, error) { return &BaseRead{ Output: t.Output().Var().VarID, UserSpace: t.UserSpace, Path: t.Path, Option: t.Option, }, nil } type BaseReadDynNode struct { dag.NodeBase From ioswitch2.From UserSpace jcstypes.UserSpaceDetail Option stgtypes.OpenOption } func (b *GraphNodeBuilder) NewBaseReadDyn(from ioswitch2.From, userSpace jcstypes.UserSpaceDetail, opt stgtypes.OpenOption) *BaseReadDynNode { node := &BaseReadDynNode{ From: from, UserSpace: userSpace, Option: opt, } b.AddNode(node) node.OutputStreams().Init(node, 1) node.InputValues().Init(1) return node } func (t *BaseReadDynNode) GetFrom() ioswitch2.From { return t.From } func (t *BaseReadDynNode) FileInfoSlot() dag.ValueInputSlot { return dag.ValueInputSlot{ Node: t, Index: 0, } } func (t *BaseReadDynNode) Output() dag.StreamOutputSlot { return dag.StreamOutputSlot{ Node: t, Index: 0, } } func (t *BaseReadDynNode) GenerateOp() (exec.Op, error) { return &BaseReadDyn{ UserSpace: t.UserSpace, Output: t.Output().Var().VarID, FileInfo: t.FileInfoSlot().Var().VarID, Option: t.Option, }, nil } type BaseWriteNode struct { dag.NodeBase To ioswitch2.To UserSpace jcstypes.UserSpaceDetail Path jcstypes.JPath Option stgtypes.WriteOption } func (b *GraphNodeBuilder) NewBaseWrite(to ioswitch2.To, userSpace jcstypes.UserSpaceDetail, path jcstypes.JPath, opt stgtypes.WriteOption) *BaseWriteNode { node := &BaseWriteNode{ To: to, UserSpace: userSpace, Path: path, Option: opt, } b.AddNode(node) node.InputStreams().Init(1) node.OutputValues().Init(node, 1) return node } func (t *BaseWriteNode) GetTo() ioswitch2.To { return t.To } func (t *BaseWriteNode) Input() dag.StreamInputSlot { return dag.StreamInputSlot{ Node: t, Index: 0, } } func (t *BaseWriteNode) FileInfoVar() dag.ValueOutputSlot { return dag.ValueOutputSlot{ Node: t, Index: 0, } } func (t *BaseWriteNode) GenerateOp() (exec.Op, error) { return &BaseWrite{ Input: t.InputStreams().Get(0).VarID, UserSpace: t.UserSpace, Path: t.Path, FileInfo: t.FileInfoVar().Var().VarID, Option: t.Option, }, nil }