英文:
golang-migrate - skip migrations if DB has newer migration version
问题
我有一个场景,如果我回滚到应用程序的先前版本,迁移步骤将失败,并显示有关缺少脚本的错误,例如error: no migration found for version 10 error: file does not exist
。
我不希望回滚数据库更改,只是在这种情况下跳过运行迁移步骤。
我尝试在我的 entrypoint.sh
代码中实现一个简单的检查,但是 golang-migrate
似乎没有提供一种通过 cli 检索(并保留)DB 迁移版本的方法。
有一个 version
命令,我在下面的示例中尝试过,它会打印当前版本。然而,该值无法保存到变量中 - 我认为该命令在运行后会断开与数据库的连接并清除检索到的版本。
#!/bin/bash
set -euo pipefail
MIGRATION_COUNT=$(find /app/config/db/migrations/*.up.sql | wc -l)
echo 'MIGRATION_COUNT: ' $MIGRATION_COUNT
CURRENT_VERSION=$(/app/migrate -source=file:///app/config/db/migrations/ -database=<connection_string> version)
echo 'CURRENT_VERSION: ' $CURRENT_VERSION
if [ "$MIGRATION_COUNT" -gt "$CURRENT_VERSION" ]; then
/app/migrate \
-source=file:///app/config/db/migrations/ \
-database=<connection_string> \
up
fi
/app/my-app
上述脚本的输出(你可以看到当前版本是 10
,打印出来但没有保存到 CURRENT_VERSION 变量中):
Attaching to my_app
my_app | MIGRATION_COUNT: 9
my_app | 10
my_app | CURRENT_VERSION: [2021-09-06T03:00:55.8023451Z] [INFO] [app=myapp-migrate] Migrating database{"host":"db","port":5432,"dbname":"myappname","user":"myapp","password":"XXXXX","tls":{"mode":"disable"},"maxconn":25,"maxidle":3}
my_app | /app/entrypoint.sh: line 11: [: [2021-09-06T03:00:55.8023451Z] [INFO] [app=myapp-migrate] Migrating database{"host":"db","port":5432,"dbname":"myappname","user":"myapp","password":"XXXXX","tls":{"mode":"disable"},"maxconn":25,"maxidle":3}: integer expression expected
想知道是否有人知道如何在 bash 脚本中检索当前版本。如果没有,是否有其他方法来实现跳过迁移步骤?我没有找到使用 golang-migrate
库实现此类逻辑的任何选项。
英文:
I have a scenario where if I rollback to a previous version of my application the migration step will fail with errors regarding missing script eg. error: no migration found for version 10 error: file does not exist
I do not wish to rollback the DB changes, but simply skip running the migration step in this scenario.
I have tried implementing a simple check in my entrypoint.sh
code; however, golang-migrate
does not seem to provide a way to retrieve (and retain) the DB migration version via the cli.
There is the version
command which I tried in my below example which prints the current version. However, that value cannot be saved to a variable - I think the command disconnects from the DB after running and wipes away the retrieved version.
#!/bin/bash
set -euo pipefail
MIGRATION_COUNT=$(find /app/config/db/migrations/*.up.sql | wc -l)
echo 'MIGRATION_COUNT: ' $MIGRATION_COUNT
CURRENT_VERSION=$(/app/migrate -source=file:///app/config/db/migrations/ -database=<connection_string> version)
echo 'CURRENT_VERSION: ' $CURRENT_VERSION
if [ "$MIGRATION_COUNT" -gt "$CURRENT_VERSION" ]; then
/app/migrate \
-source=file:///app/config/db/migrations/ \
-database=<connection_string> \
up
fi
/app/my-app
The output of above script (you can see the current version is 10
does print but does not save to the CURRENT_VERSION variable):
Attaching to my_app
my_app | MIGRATION_COUNT: 9
my_app | 10
my_app | CURRENT_VERSION: [2021-09-06T03:00:55.8023451Z] [INFO] [app=myapp-migrate] Migrating database{"host":"db","port":5432,"dbname":"myappname","user":"myapp","password":"XXXXX","tls":{"mode":"disable"},"maxconn":25,"maxidle":3}
my_app | /app/entrypoint.sh: line 11: [: [2021-09-06T03:00:55.8023451Z] [INFO] [app=myapp-migrate] Migrating database{"host":"db","port":5432,"dbname":"myappname","user":"myapp","password":"XXXXX","tls":{"mode":"disable"},"maxconn":25,"maxidle":3}: integer expression expected
Wondering if anyone knows how I can retrieve the current version in the bash script. If not, is there another way to implement the skipping of the migration step? I was not been able to find any options for that sort of logic using golang-migrate
library
答案1
得分: 1
为了解决这个问题,你首先必须明白$()
只能捕获标准输出(stdout)。所以,如果你执行一个将结果写入标准输出的函数,你的解决方案是完全正确的,并且可以工作。由于你的解决方案在我看来是正确的,我查看了你正在使用的工具的代码,结果发现他们做了一些混乱的事情。据我所见,几乎每个输出,无论是普通输出还是错误输出,都是通过使用Go的log
包来写入的。然而,log包的特性是默认使用stderr
通道而不是stdout
通道,这当然是正确的。
这里是他们(go-migrate)实现的Println
函数的代码,他们用它来打印你试图捕获的版本字符串。
所以错误不在于你,而在于go-migrate的开发人员。按照惯例,只有错误应该写入stderr,而像你的情况下的版本号这样的一般输出应该写入stdout。
为了绕过这个问题,你可以尝试像这样的解决方案:
CURRENT_VERSION=$(/app/migrate -source=file:///app/config/db/migrations/ -database=<connection_string> version 2>&1)
注意:这个解决方案并不安全,因为现在你将stderr发送到了stdout,所以你可能会捕获到真正的错误而不仅仅是版本号。所以要小心!
英文:
To solve this, you must first understand that $()
ONLY captures stdout. So if you execute a function that writes its result to stdout, your solution is perfectly correct and would work. Since your solution seemed correct to me, I looked at the code from the tool you are using and lo and behold, they are doing something rather messy. As far as I can see, almost every output no matter if regular or error is written through the use of Go's log
package. However, the log package has the property to use the channel stderr
instead of stdout
by default - correctly of course.
Here is their (go-migrate's) implementation of the Println
function that they use to print the version string that you're trying to capture.
So the error is less with you than with the developers of go-migrate. By convention, only errors should be written to stderr and general output such as the version number in your case should be written to stdout.
To work around this anyway, you could try something like this:
CURRENT_VERSION=$(/app/migrate -source=file:///app/config/db/migrations/ -database=<connection_string> version 2>&1)
> Note: This solution is NOT safe because you are now sending stderr in stdout and so you may be capturing real errors in the variable and not just the version number. So be careful!
<h1></h1>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论