Azure Devops Build Task, выполнение сценария SQL прерывается после нескольких вызовов sp_rename - PullRequest
2 голосов
/ 28 мая 2020

Я хочу создать задачу сборки Azure Devops, которая выполняет список из SQL сценариев для базы данных SQL Server 2017. Я следовал этому руководству, чтобы создать задачу: https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=azure-devops

Задача обычно выполняется успешно, и я уже выполнил различные сценарии для моей локальной базы данных (в SQL Server 2017 Express) . Я использую npm пакет «mssql / msnodesqlv8» (собственный SQL серверный драйвер для ms sql) для подключения и выполнения скриптов непосредственно в node.js.

async function executeBatches(script: string, pool: sql.ConnectionPool) {
    const batches = script.split("\r\nGO");
    for (const batch of batches) {
        console.log("Executing script:", batch);
        await pool.batch(batch);
    }
}

Теперь я нашел скрипт, который очень странным образом дает сбой. Рассматриваемый сценарий выполняет ряд переименований более 4 таблиц в транзакции, например:

BEGIN TRANSACTION
EXEC sp_rename 'table' , 'newTable'
EXEC sp_rename 'newTable.column', 'newColumn', 'COLUMN'
(repeat for several columns)
EXEC sp_rename 'dbo.PK_table', 'PK_newTable'

(repeat for 3 more tables)
COMMIT

В SQL Server Management Studio сценарий выполняется правильно. Но в задаче Devops этот скрипт прерывается примерно после 18 sp_rename вызовов. Ошибка не возникает, и транзакция остается открытой. Клиент продолжит работу (поскольку ошибок не было) и после выполнения еще нескольких запросов SQL Сервер инициирует откат и, конечно, откатит все изменения с момента выполнения этого сценария.

Я переключил операторы в script и попытался закомментировать некоторые строки, но он всегда прерывается примерно после 18 sp_rename вызовов. Когда я удаляю достаточно строк, чтобы было 18 или менее вызовов sp_rename, задача может полностью запустить сценарий и зафиксировать изменения (неважно, какие строки).

Когда я удалю транзакцию, она выполнит все переименования до этого магического числа, а затем все еще прерывают скрипт и оставляют неявную транзакцию из последнего оператора открытой, чтобы он все равно откатил все изменения после еще нескольких запросов.

Я запустил SQL Profiler, и он показывает StmtStarting для переименования, а затем BatchCompleted с ошибкой «2 - Abort», но нет другой ошибки или причины, по которой пакет был прерван.

Сеанс system_health показывает 2 ошибки при выполнении сценария:

Ошибка # 1

Ошибка подключения с tds_flags «DisconnectDueToReadError, NetworkErrorFoundInInputStream, NormalDisconnect» и tds_input_buffer_error 109.

*

* Ошибка # 2

Ошибка безопасности с кодом ошибки 5023 (что означает «Группа или ресурс e находится в неправильном состоянии для выполнения запрошенной операции. ")

Поиск этих ошибок в Интернете не дал полезных результатов, поскольку они либо не имеют решения, либо связаны с проблемами входа в систему, которые, как я считаю, не случай, поскольку я могу нормально выполнять другие скрипты.

Я также уже проверил кодировку и правильно ли читается скрипт через библиотеку узлов «fs».

Любая справка или указатели на то, где я мог бы найти причину этой проблемы, мы будем очень признательны.

РЕДАКТИРОВАТЬ: Я нашел время для создания небольшого примера с помощью только msnodesqlv8.

import tl = require("azure-pipelines-task-lib/task");
import fs = require("fs");
import path = require("path");
import util = require("util");

import { SqlClient } from "msnodesqlv8";
// tslint:disable-next-line: no-var-requires
const sqlClient: SqlClient = require("msnodesqlv8");
const open = util.promisify(sqlClient.open);
const query = util.promisify(sqlClient.query);

async function run() {
    try {
        const scriptDirectory = tl.getInput("ScriptDirectory", true) ?? "";
        const connectionString = "Driver={ODBC Driver 13 for SQL Server};Server={.\\SQLEXPRESS};Uid={sa};Pwd={start};Database={MyDatabase};Encrypt={yes};TrustServerCertificate={yes}";
        const scriptsDir = fs.readdirSync(scriptDirectory);
        const con = await open(connectionString);
        const close = util.promisify(con.close);
        try {
            const conQuery = util.promisify(con.query);
            for (const file of scriptsDir) {
                console.log("Executing:", file);
                const script = readFileWithoutBom(path.join(scriptDirectory, file));
                console.log("Executing script:", script);
                // await query(connectionString, { query_str: script, query_timeout: 120 });
                await conQuery({ query_str: script, query_timeout: 120 });

                const insert = `INSERT INTO AppliedDatabaseScript (ScriptFile, DateApplied) VALUES ('${file}', GETDATE())`
                console.log("Executing script:", insert);
                // await query(connectionString, { query_str: insert });
                await conQuery({ query_str: insert });
            }
        } finally {
            await close();
        }
    }
    catch (err) {
        console.error(err);
        tl.setResult(tl.TaskResult.Failed, err.message);
    }
}

// Strip BOM. SQL Server won't execute certain scripts with BOM.
function readFileWithoutBom(filePath: string) {
   return fs.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, "");
}

Поведение остается прежним. Я пробовал с общим подключением и отдельным подключением для каждого запроса. Он откатит все в том же соединении и продолжит работу, как если бы ошибки не было. Я также повозился с тайм-аутом запроса, но это не имеет никакого отношения к ошибке.

1 Ответ

0 голосов
/ 16 июня 2020

Мне удалось заставить его работать, переключившись на неродной драйвер tedious (также поддерживается ms sql). Кажется, что собственный SQL серверный драйвер для node.js работает некорректно.

В случае, если у кого-то возникнут проблемы с подключением к утомительным (так как я не смог найти надлежащую документацию в Интернете), это основные c параметры конфигурации:

{
    user: "username",
    password: "password",
    server: "hostname\\instancename",
    database: "database",
    port: 1433
}

Вам необходимо убедиться, что SQL Server Browser запущен и экземпляр настроен для удаленного доступа (даже при подключении через localhost) путем активации TCP / IP в SQL Диспетчер конфигурации сервера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...