英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论