Предотвращение потерянных запросов sql с веб-сайта - PullRequest
0 голосов
/ 09 мая 2018

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

Итак, допустим, что нагрузка на БД высока, и пользователь разочарован тем, сколько времени занимает загрузка отчета на странице, и многократно обновляет страницу, начиная с новых выполнений той же хранимой процедуры.

Вот код, который мы используем для выполнения хранимой процедуры и управления соединением:

using (SqlCommand cmd = CreateCommand(qry, CommandType.StoredProcedure, args))
            {
                SqlDataAdapter adapt = new SqlDataAdapter(cmd);
                DataSet ds = new DataSet();
                adapt.Fill(ds);
                return ds;
            }

Старые исполнения все еще попадают в базу данных, даже когда пользователь покидает страницу или обновляет ее, порождая новую.

Два возможных решения:

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

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

1 Ответ

0 голосов
/ 18 мая 2018

В итоге я использовал Async Actions с токенами отмены.


Сам метод действия MVC:

[AsyncTimeout(600000)] // Necessary to avoid the too-short 45 second default timeout 
                       // (which also results in successful cancellation of the sql command 
                       // by the way in case that's something you want)
[Route("my-report")]
public async Task<string> MyReport(CancellationToken cancellationToken) 
{
    // These two lines are a workaround - necessary for page refreshes to trigger cancellation in MVC 5
    CancellationToken disconnectedToken = Response.ClientDisconnectedToken;
    var token = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, disconnectedToken).Token;

    // get the data
    var response = await dbService.ExecDataSetProcAsync("stored_proc_name", cancellation token, storedProcedureArguments);

    // returning the page with whatever data
    return response;
}

Логика выполнения и отмены хранимой процедуры: (Я исключаю логику для построения SQLCommand в CreateCommand (), чтобы этот пример был кратким)

public async Task<DataSet> ExecDataSetProcAsync(string qry, CancellationToken cancellationToken, params object[] args)
        {
            using (SqlCommand cmd = CreateCommand(qry, CommandType.StoredProcedure, args))
            {
                DataSet ds = new DataSet();

                await Task.Run(() =>
                {
                    SqlDataAdapter adapt = new SqlDataAdapter(cmd);
                    cancellationToken.Register(() => cmd.Cancel());

                    try
                    {
                        adapt.Fill(ds);
                    }
                    catch (SqlException e)
                    {
                        // Canceling the sqlCommand causes an exception.  This is a workaround 
                        // to catch it, in case you don't want to be throwing the exception.
                        if (!cancellationToken.IsCancellationRequested)
                        {
                            throw e;
                        }
                    }
                });

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