Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

建议:session 接口增加 ctx 参数 #21

Open
xmx opened this issue Sep 6, 2023 · 2 comments
Open

建议:session 接口增加 ctx 参数 #21

xmx opened this issue Sep 6, 2023 · 2 comments

Comments

@xmx
Copy link
Contributor

xmx commented Sep 6, 2023

方法调用时传递 ctx 可以由调用者灵活控制。
在一些特殊情况下,例如:
将 session 存在了数据库或 redis 等外部系统中,由于网络波动或性能差,导致 session 操作耗时,
客户端可能超时或主动取消了请求,但是没有 ctx 的控制,session 操作仍然可能还在操作中。
虽然 session 实现者可以指定一个带超时时间的 ctx,但是该 ctx 很难与客户端的请求的 ctx 产生关联(http.Request.Context())。

效果如下:

type Session interface {
	GetSession(ctx context.Context, id string) (value interface{}, err error)
	SetSession(ctx context.Context, id string, value interface{}) error
	DelSession(ctx context.Context, id string) error
}

ship.Context 实现可改为:

func (c *Context) GetSession(ctx context.Context, id string) (v interface{}, err error) {
	if ctx == nil {
		ctx = c.req.Context()
	}
	if id == "" {
		err = ErrInvalidSession
	} else if v, err = c.Session.GetSession(ctx, id); err == nil && v == nil {
		err = ErrSessionNotExist
	}

	return
}

func (c *Context) SetSession(ctx context.Context, id string, value interface{}) (err error) {
	if id == "" || value == nil {
		return ErrInvalidSession
	}
	if ctx == nil {
		ctx = c.req.Context()
	}
	return c.Session.SetSession(ctx, id, value)
}

func (c *Context) DelSession(ctx context.Context, id string) (err error) {
	if id == "" {
		return ErrInvalidSession
	}
	if ctx == nil {
		ctx = c.req.Context()
	}
	return c.Session.DelSession(ctx, id)
}
@xgfone
Copy link
Owner

xgfone commented Sep 10, 2023

这个需要改变 Session 类型的签名,导致与当前版本不兼容,需要将版本从 /v5 升级到 /v6

添加 context.Context 最大的用途就是自定义超时,防止 Session 操作过长或一直阻塞。

但当前也有方法支持相应的功能:在不改变当前接口兼容性的前提下,在实现 Session 接口时,可以支持一个超时时间,即每当调用 Session 接口的方法时,对整个调用设置指定的超时时间,大致逻辑如下:

type redisSession struct {
	timeout time.Duration
	rclient *redis.Client
}

func (s *redisSession) GetSession(id string) (value interface{}, err error) {
	ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
	defer cancel()

	// TODO: ...
}

func (s *redisSession) SetSession(id string, value interface{}) error {
	ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
	defer cancel()

	// TODO: ...
}

func (s *redisSession) DelSession(id string) error {
	ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
	defer cancel()

	// TODO: ...
}

@xmx
Copy link
Contributor Author

xmx commented Jan 12, 2024

当前我就是用上面的方式控制 session 操作的超时时间,这个 timeout 算是基于经验设置的超时时间。
举个极端的例子,比如:timeout 设置的是一分钟,此时数据库查询的很慢需要很久。
当前端请求或许刷新页面、或许主动断开连接、或许网络中断。按道理当前端一旦断开,session 查询可以立即终止。
但是 session 的 ctx 和 http 的 ctx 没有建立关联,session 操作依然在进行,要等到自定义 ctx cancel,或者是数据库结果返回,或者是其他异常原因。
虽然自定义超时时间不影响系统运行,但是如果传入一个 http ctx 能够更优雅的串联起 session 逻辑,也能留给开发者更多的选择。
这个功能会改变 Session 接口的签名,会影响 ship 的兼容性,确实不太适合在小版本中引入。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants