Cloud SQL可以同步导出吗?或者,如果是异步的,如何捕获并重试故障?

时间:2019-05-15 10:18:54

标签: node.js async-await google-cloud-functions export-to-csv google-cloud-sql

简短摘要

Cloud SQL导出有时可能会失败。
是否可以使导出请求同步运行,从而使故障很容易重试?
还是有一种很好的方法以异步方式重试导出?

完整说明

我正在将代码从App Script迁移到Node.js,但遇到了问题。该代码将Cloud SQL查询的结果导出到CSV文件中。 Cloud 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);
        }
    });
});

这意味着错误会导致立即失败。我找不到将错误交给父/调用函数进行重试管理的方法。

1 个答案:

答案 0 :(得分:0)

解决方案

最后,我尝试了一种递归方法。我最初认为这是不可能的,因为编译器抱怨使用异步和等待,但是我只需要找到放置它们的正确位置。

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));
}