开发应用程序的新版本¶
本主题提供将应用程序更新到新版本或补丁时的信息和最佳实践。
开发新版本或补丁时的最佳实践¶
在开发应用程序新版本或补丁时,提供商应考虑以下最佳实践。
启动自动安全扫描前,全面测试应用程序¶
以下操作可以启动自动安全扫描:
如果存在应用程序版本,请将应用程序包的 DISTRIBUTION 属性设置为 EXTERNAL
将新版本或补丁添加到将 DISTRIBUTION 属性设置为 EXTERNAL 的应用程序包中
Snowflake 建议您在启动安全扫描前,在本地对应用程序的新版本或补丁进行全面测试,以避免扫描失败时产生延迟和多次重复扫描。
确保各版本之间的兼容性¶
提供商必须确保应用程序的新版本与旧版本兼容。例如,如果应用程序有版本 v1 和 v2,那么 v2 必须与 v1 兼容。添加 v3 版本时,v3 版本必须与 v2 版本兼容。不过,由于一个应用程序同一时间只能存在两个版本,因此 v3 版本不必与 v1 版本兼容。
在前一版本中运行的代码必须能够应对新版本中引入的状态变化。要应对无状态对象,提供商应使用版本化架构,确保正确处理升级。有关更多信息,请参阅 使用版本化架构跨版本管理应用程序对象。
尽量减少补丁中的状态变化¶
提供商必须确保新补丁不会引入与相同版本的之前补丁不同的状态变化。提供商在开发补丁时,必须尽量减少状态变化,如添加或更改表或列。表和列必须在所有版本和补丁中兼容。补丁应侧重于错误修复或小功能添加,不涉及状态修改。
状态变化只能在更新应用程序版本时进行。
从安装脚本创建对象时,请遵循安全实践¶
从安装脚本创建对象时,请考虑以下最佳实践:
使用 CREATE IF NOT EXISTS:
创建表、视图、函数或过程等数据库对象时,因适用情况而定,应始终使用 CREATE OR REPLACE、CREATE IF NOT EXISTS 或 CREATE OR ALTER。这样可以防止在升级过程中尝试创建已存在的对象时出现错误。
Snowflake 建议仅对无状态对象(如函数或过程)使用 CREATE OR REPLACE,而不对有状态对象(如表)使用。
确保每个应用程序的安装脚本都是独立的
应用程序的每个版本都必须完整且独立。例如,如果在 v2.0 版本中使用 CREATE TABLE IF NOT EXISTS a(int c) 创建了一个表,而 v3.0 版本包含 ALTER TABLE A(...),则应确保 v3.0 版本中具有 CREATE TABLE 和 ALTER TABLE 语句。这可确保从后续版本安装应用程序的用户具有所有必要的架构和对象。
在安装脚本中仅使用幂等变更
将 CREATE 和 ALTER 语句结构化,使其具有幂等性,以便多次运行时不会出错,也不会产生意外的副作用。如果安装脚本在安装时失败,Snowflake 会从头开始重新运行安装脚本。如果已为该版本创建版本架构,则不会重新创建或删除。因此,提供商应使用 CREATE IF NOT EXISTS 版本的 CREATE 命令。
例如:
使用 ALTER TABLE ADD COLUMN IF NOT EXISTS 可确保仅在列不存在的情况下添加列。
插入行时,应采取保障措施,防止无意中出现重复行,因为升级可能会重试多次。
创建或删除应用程序角色时应谨慎¶
在版本或补丁中创建或删除应用程序角色时要谨慎。应用程序角色没有版本控制。从一个版本到另一个版本时,删除应用程序角色或撤销对象上的授权可能会导致应用程序停止工作或阻止使用者访问应用程序。
避免使用 CREATE OR REPLACE APPLICATION ROLE。相反,请使用 CREATE APPLICATION ROLE IF NOT EXISTS。OR REPLACE 子句将删除并重新创建角色,这会导致权限问题,因为需要重新授予之前版本中授予应用程序角色的账户级角色。
使用容器开发应用程序的新补丁或新版本时的最佳实践¶
使用容器开发应用程序新版本或补丁时,提供商应考虑以下最佳实践:
在设置 SYSTEM$WAIT_FOR_SERVICES 系统函数的超时值时请谨慎。
如果将此值设置得过长,可能会导致应用程序的其他部分无法使用服务。有关更多信息,请参阅 暂停执行安装脚本。
Snowflake 建议在版本化架构中创建版本初始化存储过程。如果版本初始化程序不是在版本化架构中创建的,那么从一个版本到下一个版本,版本初始化程序可能不存在。
如果应用程序指定了版本初始化程序,Snowflake 建议应用程序尝试在版本初始化程序中启动或升级服务,而不是在安装脚本中。这样可以确保,升级尝试失败时,运行的是正确版本的服务。
版本初始化程序无需授予应用程序角色。
有关使用容器更新应用程序的更多信息,请参阅 使用容器更新应用程序。
使用容器更新应用程序¶
使用容器将应用程序更新到新版本时,升级过程中会有额外的注意事项。使用容器升级应用程序的过程主要分为两个阶段:
升级应用程序管理的容器中的服务。
与其他 Snowpark Container Services 一样,容器应用程序使用 ALTER SERVICE 命令,根据新版本的服务规范文件修改服务。该命令以异步方式运行。
升级应用程序中的其他对象。
服务成功升级后,应用程序中的其他对象也会随之升级。这与一般 Snowflake Native App 升级过程类似。有关更多信息,请参阅 关于应用程序升级。
Snowflake Native App Framework 允许用户在重大版本升级期间继续使用应用程序,确保正常应用程序不会停机。但是,对于使用容器的应用程序而言,CREATE SERVICE 和 ALTER SERVICE 都是异步。这意味着,即使升级完成后,新版本的服务也可能无法立即提供。
在使用容器升级应用程序时,潜在的问题是 ALTER SERVICE 命令异步运行。如果该命令将 ALTER SERVICE 直接添加到安装脚本中,则安装脚本会在服务升级过程中继续运行。
提供商在编写安装脚本时,应假定服务升级可能尚未完成,或者应使用 SYSTEM$WAIT_FOR_SERVICES 和 使用版本初始化程序管理服务升级 来保证服务的正确版本已准备就绪,可供使用。
为了正确处理服务升级,Snowflake Native App Framework 提供了一些功能,让应用程序可以:
暂停执行安装脚本,直到服务升级成功或失败。提供商应确保安装脚本能够处理可能出现的情况。有关更多信息,请参阅 暂停执行安装脚本。
如果升级失败,可使用版本初始化程序函数将服务升级回滚到之前的版本。有关更多信息,请参阅 升级服务时的注意事项。
暂停执行安装脚本¶
为尽量减少停机时间并确保服务就绪,请在创建或更改服务后使用安装脚本中的 SYSTEM$WAIT_FOR_SERVICES 系统函数:
SELECT SYSTEM$WAIT_FOR_SERVICES(600, 'services.web_ui', 'services.worker', 'services.aggregation');
此命令会暂停安装脚本,直到出现下列情况之一:
所有传递给系统函数的命名服务都具有 READY 状态。
任何命名的服务都有 FAILED 状态。
600 秒已过。
该系统函数可确保应用程序的安装或升级等到服务可用或发生故障,从而保障服务状态与版本升级同步。
升级服务时的注意事项¶
Snowflake Native App Framework 提供了版本初始化程序回调函数,允许提供商将升级服务与升级过程的其他部分同步。
在基本应用程序升级的过程中,安装脚本会通过修改版本化架构中的对象来升级到新版本的应用程序。如果升级过程中发生错误,版本化架构中的对象会恢复到上一版本的应用程序。
对于使用容器的应用程序,通过运行安装脚本中的 CREATE SERVICE 或 ALTER SERVICE 命令创建或修改的服务会使用新版本的服务规范文件。
由于服务不是在版本化架构中创建的,因此只要 CREATE SERVICE 或 ALTER SERVICE 命令运行成功,服务就会升级。例如,如果安装脚本稍后出现故障,则版本化架构中的对象会恢复回之前的版本,但修改后的服务是新版本的服务。
使用版本初始化程序管理服务升级¶
Snowflake Native App Framework 提供了一个版本初始化程序,用于启动或升级服务或其他相关过程,例如任务。版本初始化程序是一个在清单文件中指定的回调存储过程。
系统会在以下情况下调用版本初始化程序:
安装过程中,应用程序安装脚本无误完成后,系统就会调用版本初始化程序。
升级过程中,有两种可能的情况会调用版本初始化程序:
如果新版本的安装脚本成功,则会调用新版本应用程序的版本初始化程序。
如果新版本的安装脚本或版本初始化程序失败,则会调用前一版本应用程序的版本初始化程序。这样,前一版本的版本初始化程序就可以使用 ALTER SERVICE 将服务恢复到前一版本。
为应用程序添加版本初始化程序¶
要指定用作版本初始化程序的存储过程,请在 manifest.yml
文件中添加以下内容:
lifecycle_callback:
version_initializer: callback.version_init
在本例中,version_initializer
属性设置为名为 callback
的架构中名为 version_init
的存储过程。
在安装脚本中,提供商可在版本化架构中定义该过程,如下例所示:
CREATE OR ALTER VERSIONED SCHEMA callback;
CREATE OR REPLACE PROCEDURE callback.version_init()
...
-- body of the version_init() procedure
...