使用Go安全地执行数据库迁移

huangapple go评论78阅读模式
英文:

Safely perform DB migrations with Go

问题

假设我有一个 Web 应用程序,显示一个帖子列表。帖子的结构如下:

type Post struct {
    Id         int64 `sql:",primary"`
    Title      string
    Body       string
}

通过以下方式检索帖子:

var posts []*Post
rows, err := db.QueryContext(ctx, "select * from posts;")
if err != nil {
    return nil, oops.Wrapf(err, "could not get posts")
}

defer rows.Close()

for rows.Next() {
    p := &Post{}
    err := rows.Scan(
        &p.Id,
        &p.Title,
        &p.Body,
    )
    if err != nil {
        return nil, oops.Wrapf(err, "could not scan row")
    }
    posts = append(posts, p)
}

return posts, nil

一切正常。现在,我想通过添加一个列来修改表结构:

ALTER TABLE posts ADD author varchar(62);

突然间,获取帖子的请求结果如下:

sql: expected 4 destination arguments in Scan, not 3

这是有道理的,因为表现在有4列,而不是检索逻辑规定的3列。

然后,我可以更新结构体如下:

type Post struct {
    Id         int64 `sql:",primary"`
    Title      string
    Body       string
    Author     string
}

并更新检索逻辑如下:

for rows.Next() {
    p := &Post{}
    err := rows.Scan(
        &p.Id,
        &p.Title,
        &p.Body,
        &p.Author
    )
    if err != nil {
        return nil, oops.Wrapf(err, "could not scan row")
    }
    posts = append(posts, p)
}

这样问题就解决了。然而,这意味着在迁移和逻辑更新+部署之间总会有一段停机时间。如何避免这种停机时间呢?

我尝试交换上述更改的顺序,但这并不起作用,因为相同的请求结果如下:

sql: expected 3 destination arguments in Scan, not 4

(这是有道理的,因为此时表只有3列,而不是4列);

其他请求结果如下:

Error 1054: Unknown column 'author' in 'field list'

(这是有道理的,因为此时 posts 表还没有 author 列)

英文:

Let's say I have a web app that shows a list of posts. The post struct is:

type Post struct {
	Id         int64 `sql:",primary"`
	Title      string
	Body       string
}

It retrieves the posts with:

	var posts []*Post
	rows, err := db.QueryContext(ctx, "select * from posts;")
	if err != nil {
		return nil, oops.Wrapf(err, "could not get posts")
	}

	defer rows.Close()

	for rows.Next() {
		p := &Post{}
		err := rows.Scan(
			&p.Id,
			&p.Title,
			&p.Body,
		)
		if err != nil {
			return nil, oops.Wrapf(err, "could not scan row")
		}
		posts = append(posts, p)
	}

	return posts, nil

All works fine. Now, I want to alter the table schema by adding a column:

ALTER TABLE posts ADD author varchar(62);

Suddenly, the requests to get posts result in:

sql: expected 4 destination arguments in Scan, not 3

which makes sense since the table now has 4 columns instead of the 3 stipulated by the retrieval logic.

I can then update the struct to be:

type Post struct {
    Id         int64 `sql:",primary"`
    Title      string
    Body       string
    Author     string
}

and the retrival logic to be:

    for rows.Next() {
        p := &Post{}
        err := rows.Scan(
            &p.Id,
            &p.Title,
            &p.Body,
            &p.Author
        )
        if err != nil {
            return nil, oops.Wrapf(err, "could not scan row")
        }
        posts = append(posts, p)
    }

which solves this. However, this implies there is always a period of downtime between migration and logic update + deploy. How to avoid that downtime?

I have tried swapping the order of the above changes but this does not work, with that same request resulting in:

sql: expected 3 destination arguments in Scan, not 4

(which makes sense, since the table only has 3 columns at that point as opposed to 4);

and other requests resulting in:

Error 1054: Unknown column 'author' in 'field list'

(which makes sense, because at that point the posts table does not have an author column just yet)

答案1

得分: 2

你可以通过调整 SQL 查询来实现你想要的行为,以返回你想要填充的确切字段。

SELECT Id, Title, Body FROM posts;

这样,即使你添加了另一个列 Author,查询结果仍然只包含 3 个值。

英文:

You should be able to achieve your desired behavior by adapting the SQL Query to return the exact fields you want to populate.

SELECT Id , Title , Body FROM posts;

This way even if you add another column Author the query results only contain 3 values.

huangapple
  • 本文由 发表于 2021年10月18日 05:26:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/69608727.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定