Cloud SQL 可以同步导出吗?或者如果是异步的,如何捕获并重试失败?
Can Cloud SQL be exported synchronously? Or how can failures be caught and retried if asynchronous?
简短摘要
云 SQL 导出有时会失败。
我能否使导出请求同步运行,以便轻松重试失败?
或者是否有一种以异步方式重试导出的好方法?
完整描述
我正在将代码从 App Script 迁移到 Node.js,我遇到了一个问题。该代码将 Cloud SQL 查询的结果导出到 CSV 文件中。云 SQL 无法进行并行导出,因此这有时会导致抛出以下错误:
Error: Operation failed because another operation was already in progress.
在 App Script 中,方法是等待 6 秒然后重试,重试次数限制为 10 次。
这样做很简单,因为代码是同步运行的:
for(exportAttempt=1; exportAttempt<=10; exportAttempt++) {
Utilities.sleep(6000);
// Use the url fetch service to issue the https request and capture the response
var response = UrlFetchApp.fetch(api, parameters);
response = JSON.parse(response);
if(exportAttempt == 10) {
throw('Exceeded the limit of 10 failed export requests to REST API.');
}
if(response.status != undefined) {
_log_('DEBUG', 'Export attempt ' + exportAttempt + ' successful.');
exportAttempt=10;
}
if(response.error != undefined) {
_log_('DEBUG', 'Attempt number ' + exportAttempt + ' errored. ' + JSON.stringify(response));
}
}
使用以下代码可以复制 Node.js 中的导出功能,但它的行为是异步的:
var {google} = require('googleapis');
var sqladmin = google.sqladmin('v1beta4');
var uri = 'gs://' + csBucket + '/' + csFileName;
google.auth.getApplicationDefault(function(err, authClient) {
if (err) {
_log_('ERROR', 'Authentication failed because of ' + err);
return false;
}
if (authClient.createScopedRequired && authClient.createScopedRequired()) {
var scopes = [
'https://www.googleapis.com/auth/cloud-platform',
'https://www.googleapis.com/auth/sqlservice.admin'
];
authClient = authClient.createScoped(scopes);
}
var request = {
project: projectId,
instance: sqlInstance,
resource: {
exportContext: {
kind: "sql#exportContext",
fileType: fileType,
uri: uri,
databases: [sqlSchema],
csvExportOptions: {
selectQuery: exportSQL
}
}
},
auth: authClient
};
sqladmin.instances.export(request, function(err, result) {
if (err) {
//The problem with the exception is that it stops the Cloud Function.
//It isn't thrown up to the parent/calling function to be used for a retry.
_log_('ERROR', 'Export failed because of ' + err);
throw(err)
} else {
_log_('DEBUG', 'result');
_log_('DEBUG', result);
}
});
});
这意味着错误导致立即失败。我找不到将错误抛出到 parent/calling 函数以通过重试进行管理的方法。
解决方案
最后我尝试了递归的方法。我最初认为这是不可能的,因为编译器抱怨使用 async 和 await 但我只需要找到合适的位置来放置它们。
sqladmin.instances.export(request, async function(err, result) {
if (err) {
if(attempt == 10) throw('Retries exceeded');
_log_('ERROR', 'Export error. Retry attempt ' + attempt);
await _sleep_(6000);
attempt++;
await exportCSV(<all>,<the>,<same>,<parameters>,<passed>,<in>,attempt);
} else {
_log_('DEBUG', 'result');
_log_('DEBUG', result);
}
...
function _sleep_(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
简短摘要
云 SQL 导出有时会失败。
我能否使导出请求同步运行,以便轻松重试失败?
或者是否有一种以异步方式重试导出的好方法?
完整描述
我正在将代码从 App Script 迁移到 Node.js,我遇到了一个问题。该代码将 Cloud SQL 查询的结果导出到 CSV 文件中。云 SQL 无法进行并行导出,因此这有时会导致抛出以下错误:
Error: Operation failed because another operation was already in progress.
在 App Script 中,方法是等待 6 秒然后重试,重试次数限制为 10 次。
这样做很简单,因为代码是同步运行的:
for(exportAttempt=1; exportAttempt<=10; exportAttempt++) {
Utilities.sleep(6000);
// Use the url fetch service to issue the https request and capture the response
var response = UrlFetchApp.fetch(api, parameters);
response = JSON.parse(response);
if(exportAttempt == 10) {
throw('Exceeded the limit of 10 failed export requests to REST API.');
}
if(response.status != undefined) {
_log_('DEBUG', 'Export attempt ' + exportAttempt + ' successful.');
exportAttempt=10;
}
if(response.error != undefined) {
_log_('DEBUG', 'Attempt number ' + exportAttempt + ' errored. ' + JSON.stringify(response));
}
}
使用以下代码可以复制 Node.js 中的导出功能,但它的行为是异步的:
var {google} = require('googleapis');
var sqladmin = google.sqladmin('v1beta4');
var uri = 'gs://' + csBucket + '/' + csFileName;
google.auth.getApplicationDefault(function(err, authClient) {
if (err) {
_log_('ERROR', 'Authentication failed because of ' + err);
return false;
}
if (authClient.createScopedRequired && authClient.createScopedRequired()) {
var scopes = [
'https://www.googleapis.com/auth/cloud-platform',
'https://www.googleapis.com/auth/sqlservice.admin'
];
authClient = authClient.createScoped(scopes);
}
var request = {
project: projectId,
instance: sqlInstance,
resource: {
exportContext: {
kind: "sql#exportContext",
fileType: fileType,
uri: uri,
databases: [sqlSchema],
csvExportOptions: {
selectQuery: exportSQL
}
}
},
auth: authClient
};
sqladmin.instances.export(request, function(err, result) {
if (err) {
//The problem with the exception is that it stops the Cloud Function.
//It isn't thrown up to the parent/calling function to be used for a retry.
_log_('ERROR', 'Export failed because of ' + err);
throw(err)
} else {
_log_('DEBUG', 'result');
_log_('DEBUG', result);
}
});
});
这意味着错误导致立即失败。我找不到将错误抛出到 parent/calling 函数以通过重试进行管理的方法。
解决方案
最后我尝试了递归的方法。我最初认为这是不可能的,因为编译器抱怨使用 async 和 await 但我只需要找到合适的位置来放置它们。
sqladmin.instances.export(request, async function(err, result) {
if (err) {
if(attempt == 10) throw('Retries exceeded');
_log_('ERROR', 'Export error. Retry attempt ' + attempt);
await _sleep_(6000);
attempt++;
await exportCSV(<all>,<the>,<same>,<parameters>,<passed>,<in>,attempt);
} else {
_log_('DEBUG', 'result');
_log_('DEBUG', result);
}
...
function _sleep_(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}