Обработка ошибок в многозвенной архитектуре
От: Владик Россия  
Дата: 21.03.03 08:01
Оценка:
Привет!

Как правильно сделать сабж? Есть такая система: клиент (C++Builder) <-> сервер (Java) <-> СУБД (Interbase/Oracle). Требуются, чтобы все ошибки, возникающие на сервере или в базе в читабельном (для конечного пользователя) виде отображались на клиенте. Сейчас сделано так, что сервер генерит исключение определенного типа, а клиент анализирует этот тип и формирует сообщение. Проблема в том, что отсутствует жесткая связь между тем, что может генерить сервер и тем, что ожидает получить клиент, т.к. физически это два отдельных проекта. Кроме того, усложняется логика клиента, который вынужден анализировать типы исключений и в той или иной ситуации формировать те или иные сообщения. Т.е. мне хотелось бы на клиенте ничего не делать, кроме как показывать готовое сообщение об ошибке, полученное с сервера. В т.ч. и сообщения, сгенерированные на уровне СУБД (как?). И тут мое мнение не сходится с мнением нашего прожект-менеджера который считает, что это все относится к логике клиента, надо четко специфицировать типы исключений для каждого метода сервера и обрабатывать их на клиенте. Так вот, может кто поделится своим опытом или даст ссылки на возможнве решения?

21.03.03 11:07: Перенесено модератором из 'Прочее' в Проектирование — ХД
Как все запущенно...
Re: Обработка ошибок в многозвенной архитектуре
От: IT Россия linq2db.com
Дата: 21.03.03 19:26
Оценка:
Здравствуйте, Владик, Вы писали:

В>Т.е. мне хотелось бы на клиенте ничего не делать, кроме как показывать готовое сообщение об ошибке, полученное с сервера. В т.ч. и сообщения, сгенерированные на уровне СУБД (как?).


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

В>И тут мое мнение не сходится с мнением нашего прожект-менеджера который считает, что это все относится к логике клиента, надо четко специфицировать типы исключений для каждого метода сервера и обрабатывать их на клиенте.


Если обрабатывать все возможные варианты ошибок для каждого метода, то клиент превратится в только обрабатывалку ошибок. Это тоже не вариант.

В>Так вот, может кто поделится своим опытом или даст ссылки на возможнве решения?


Я бы сделал один метод, который выводит информацию об ошибке клиенту:

try
{
    ServerSideMethod();
}
catch (CServerException ex)
{
    HandleServerException(ex);
}


Такого варианта будет достаточно для 90% случаев. Текст сообщения об ошибке лучше формировать на сервере.

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

try
{
    ServerSideMethod();
}
catch (ServerRecordNotFoundException ex)
{
    // ...
}
catch (CServerException ex)
{
    HandleServerException(ex);
}


Но будет достаточно и того, что бы класс CServerException содержал в своём составе в том числе и тип серверного исключения, в этом случае тоже вполне можно строить логику обработки ошибок на клиенте:

try
{
    ServerSideMethod();
}
catch (CServerException ex)
{
    if (ex.Type == "ServerRecordNotFoundException")
    {
        // ...
    }
    else
    {    
        HandleServerException(ex);
    }
}


В любом случае эти вещи лучше комбирировать и не делать искуственных ограничений ни на клиенте, ни на сервере.

Самый лучший вариант конечно второй, но он зависит от используемого софта. В принципе, если все вызовы методов проходят через контролируемый шлюз, то в нём можно вполне всё это навернуть.
Если нам не помогут, то мы тоже никого не пощадим.
Re: Обработка ошибок в многозвенной архитектуре
От: Andy77 Ниоткуда  
Дата: 21.03.03 19:32
Оценка:
Здравствуйте, Владик, Вы писали:

В>Как правильно сделать сабж?


Каким образом клиент общается с сервером? RMI?
Никто не мешает обрабатывать известные клиенту ошибки и, в то же время, оставить возможность серверу кидать неизвестные клиенту исключения.

классы-исключения (воспользуемся приёмом "marker interfaces")
interface IClientException {}
interface IServerException {}
interface IDbException {}
public class ClientException extends Exception implements IClientException { ... }
public class ServerException extends Exception implements IServerException { ... } 
public class ServerDbException extends ServerException implements IDbException { ... }
public class WellKnownServerException extends Exception implements IClientException, IServerException { ... }
public class WellKnownServerDbException extends WellKnownServerException implements IDbException { ... }


клиентский код (частный случай) —

try
{
   // ... blah blah blah
   myServerObject.doSomething();
}
catch (ClientException)
{
   // handle it
}
catch (WellKnownServerException)
{
   // do something specific to this exception
}
catch (ServerDbException e)
{
   // show the picture of your db admin followed by some "f-" words
}
catch (ServerException e)
{
   Trace.writeLine("Server error : " + e.getMessage());
}
catch (Exception e)
{
   Trace.writeLine(e.getMessage());
}


в общем, всё зависит от того, как ты общаешься с сервером. Если в качестве исключения ты получаешь просто код ошибки (например, "разговаривая" с сервлетом) — можно написать helper-метод наподобие

void extractException(int code, string msg) throws ServerException
{
   switch (code)
   {
      case 0 : throw new WellKnownServerException("too many connections");
               break;
      case 1 : throw new WellKnownServerDbException("database failure");
               break;
      ...
      default : throw new ServerException(msg);
                break;
   }
}
Re[2]: Обработка ошибок в многозвенной архитектуре
От: Владик Россия  
Дата: 24.03.03 07:49
Оценка:
Здравствуйте, IT, Вы писали:

IT>А если сервер тебе кидает исключение, которое говорит, что запись в БД не найдена и тебе нужно просто пойти по другой ветке программы без всяких сообщений пользователю?


Может, тогда лучше возвращать код ошибки?

[...]
IT>Самый лучший вариант конечно второй, но он зависит от используемого софта. В принципе, если все вызовы методов проходят через контролируемый шлюз, то в нём можно вполне всё это навернуть.

Да, все вызовы проходят через "одно место". Ну почти все.
Как все запущенно...
Re[2]: Обработка ошибок в многозвенной архитектуре
От: Владик Россия  
Дата: 24.03.03 07:57
Оценка:
Здравствуйте, Andy77, Вы писали:

A>Каким образом клиент общается с сервером? RMI?


CORBA. Что такое RMI я не знаю...

A>Никто не мешает обрабатывать известные клиенту ошибки и, в то же время, оставить возможность серверу кидать неизвестные клиенту исключения.


Это все понятно. Инетереcует именно путь специально необрабатываемой ошибки, например, из СУБД 'RISE EXCEPTION "Читабельное исключение";' до клиентского MessageBox, насколько это будет правильно и почему.
Как все запущенно...
Re[3]: Обработка ошибок в многозвенной архитектуре
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.03.03 08:34
Оценка:
Здравствуйте, Владик, Вы писали:

В>Это все понятно. Инетереcует именно путь специально необрабатываемой ошибки, например, из СУБД 'RISE EXCEPTION "Читабельное исключение";' до клиентского MessageBox, насколько это будет правильно и почему.

Неправильно. Потому что в таком случае нижний уровень архитектуры принимает решения, что делать верхнему уровню.
Исключения были сделаны вовсе не для того, чтобы можно было выкидывать красивые мессадж боксы пользователю. Не этого ему надо.
Представь себе, что ты заказываешь книгу в интернет-магазине. А тебе в итоге звонит водитель грузовика и говорит: "Чувак, моя машина выполнила недопустимую операцию. Позвони разработчикам."
... << RSDN@Home 1.0 beta 6 >>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: Обработка ошибок в многозвенной архитектуре
От: Владик Россия  
Дата: 24.03.03 08:46
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Владик, Вы писали:


В>>Это все понятно. Инетереcует именно путь специально необрабатываемой ошибки, например, из СУБД 'RISE EXCEPTION "Читабельное исключение";' до клиентского MessageBox, насколько это будет правильно и почему.

S>Неправильно. Потому что в таком случае нижний уровень архитектуры принимает решения, что делать верхнему уровню.
S>Исключения были сделаны вовсе не для того, чтобы можно было выкидывать красивые мессадж боксы пользователю. Не этого ему надо.
S>Представь себе, что ты заказываешь книгу в интернет-магазине. А тебе в итоге звонит водитель грузовика и говорит: "Чувак, моя машина выполнила недопустимую операцию. Позвони разработчикам."

Примера не понял. А как надо правильно?
Как все запущенно...
Re[5]: Обработка ошибок в многозвенной архитектуре
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.03.03 08:59
Оценка: 22 (3)
Здравствуйте, Владик, Вы писали:

В>Здравствуйте, Sinclair, Вы писали:


S>>Здравствуйте, Владик, Вы писали:


В>>>Это все понятно. Инетереcует именно путь специально необрабатываемой ошибки, например, из СУБД 'RISE EXCEPTION "Читабельное исключение";' до клиентского MessageBox, насколько это будет правильно и почему.

S>>Неправильно. Потому что в таком случае нижний уровень архитектуры принимает решения, что делать верхнему уровню.
S>>Исключения были сделаны вовсе не для того, чтобы можно было выкидывать красивые мессадж боксы пользователю. Не этого ему надо.
S>>Представь себе, что ты заказываешь книгу в интернет-магазине. А тебе в итоге звонит водитель грузовика и говорит: "Чувак, моя машина выполнила недопустимую операцию. Позвони разработчикам."

В>Примера не понял. А как надо правильно?

Ровно как говорит твой проджект-менеджер. Т.е. все, что можно, типизировать. Для крайних случаев в базовый класс исключения вносится метод генерации пользовательского представления информации об ошибке. Но это, имхо, отвратительный дизайн.

В приведенном выше примере водитель должен был позвонить начальнику службы доставки и сообщить о проблеме. Если тот достаточно полномочен, то он должен был отправить другого водителя. Или сообщить менеджеру продаж о том, что возникли проблемы с доставкой, которые он решить не в состоянии. Менеджер продаж, в свою очередь, имел бы возможность как-то разрулить ситуацию. В DHL позвонить, в конце концов. Либо (в итоге) позвонить тебе и сказать, что интернет-магазин не в состоянии выполнить заказ в данное время. Но не водитель напрямую! И не подробности о секусальной жизни автомобиля, а релевантную информацию типа того "вы можете подождать 2 недели, либо доплатить 30 баксов за курьерскую доставку, либо отказаться от заказа и получить moneyback"
... << RSDN@Home 1.0 beta 6 >>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[6]: Обработка ошибок в многозвенной архитектуре
От: Владик Россия  
Дата: 24.03.03 09:44
Оценка:
Здравствуйте, Sinclair, Вы писали:

В>>Примера не понял. А как надо правильно?

S>Ровно как говорит твой проджект-менеджер. Т.е. все, что можно, типизировать.

Все типизировать — нереально, да и не вижу никакого смысла. Какой смысл в отдельный типах исключений "нельзя удалить запись из таблицы t1" и "нельзя удалить запись из таблицы t2" (помножь на количество таблиц и количество операций с ними)? Зачем типизировать то, единственной возможной реакцией на которое может быть сообщение об ошибке? База данных имеет дело не с какими-то абстрактными таблицами, они имеют вполне конкретное отображение на предметную область. Почему тогда ошибочно формировать ошибки на уровне базы в терминах этой предметной области? Другое дело, что если действительно понадобилось анализировать ошибку и ветвить алгоритм, тогда типы будут востребованы. Твой пример как раз предполагает такую нетривиальную обработку ошибочной ситуации. В моей же ситуации 90% ошибок сервера и 95% ошибок
базы не требуют какой либо реакции, кроме отката текущего действия.
Как все запущенно...
Re[7]: Обработка ошибок в многозвенной архитектуре
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.03.03 10:14
Оценка: 7 (1)
Здравствуйте, Владик, Вы писали:

В>Все типизировать — нереально, да и не вижу никакого смысла. Какой смысл в отдельный типах исключений "нельзя удалить запись из таблицы t1" и "нельзя удалить запись из таблицы t2" (помножь на количество таблиц и количество операций с ними)?

А вот для этого и сделаны классы. Ты же не вводишь класс "покупатель Петров" и "покупатель Иванов"? Вводишь класс "покупатель".
Так же и с ошибками. Меня вот ужасно бесят эти горе-программеры, которые типизируют ошибки не глубже, чем "ошибка БД". Зашибись. Пользователю ой как интересно видеть "Error: Foreign key constraint violation (FK_TB1_StoreDept_4). Operation Terminated".
Вот если бы эти дятлы головой думали, а не долбили деревья, то мне бы прилетало типизированное исключение. Класс навроде "EForeignKeyViolation". В котором было бы:
— имя таблицы, в которой нельзя удалять запись
— ключ записи, ссылка на которую попыталась быть нарушена
— операция (delete/update/insert)
Ну и там еще чего можно придумать. Тогда бы пользователю можно было бы показать что-то вроде "Нельзя удалить склад, на котором числятся товары". Независимо от нижнего уровня.
Таких примеров — вагон. Что взять с винды, которая вечно путает "Access Denied" с "Could not Find...".

В твоем случае, если ты осознанно хочешь ограничиться туманными предсказаниями, которые будут вылетать из клиента аки мантры из молитвенной мельницы, можешь просто бросать везде самый нетипизированный ексепшн (типа как в VCL сделано. Они там очень не любят передавать с исключением параметры. Так, строку отформовали — и вперед).
... << RSDN@Home 1.0 beta 6 >>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Обработка ошибок в многозвенной архитектуре
От: Владик Россия  
Дата: 24.03.03 10:31
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Вот если бы эти дятлы головой думали, а не долбили деревья, то мне бы прилетало типизированное исключение. Класс навроде "EForeignKeyViolation". В котором было бы:

S>- имя таблицы, в которой нельзя удалять запись
S>- ключ записи, ссылка на которую попыталась быть нарушена
S>- операция (delete/update/insert)

Ну и нафига мне на клиенте имя таблицы? И вообще, зачем на клиенте знать что-то о какой-то там базе данных?

S>Ну и там еще чего можно придумать. Тогда бы пользователю можно было бы показать что-то вроде "Нельзя удалить склад, на котором числятся товары". Независимо от нижнего уровня.


Кстати, почему ты СУБД причисляешь к нижнему уровню? Это вполне себе полноценное звено системы, имеющее собственную логику.
Как все запущенно...
Re[9]: Обработка ошибок в многозвенной архитектуре
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.03.03 11:14
Оценка: 14 (1)
Здравствуйте, Владик, Вы писали:

В>Ну и нафига мне на клиенте имя таблицы? И вообще, зачем на клиенте знать что-то о какой-то там базе данных?

Ну а нафига тебе на клиенте та строка, которую я привел? А она достаточно типична. Что, пользователю что-то имя констрейнта скажет?

В>Кстати, почему ты СУБД причисляешь к нижнему уровню? Это вполне себе полноценное звено системы, имеющее собственную логику.

Потому, что ниже нее уже ничего нету. Это на самом деле неважно. Просто клиент должен показывать что-то типа "Нельзя удалить Склад №2 потому, что на нем числятся товары", а не всякое шаманство. И неважно, откуда приезжает эта строка — из базы данных, из middle tier, или ее клиент формирует. Просто информация о косяке должна передаваться снизу вверх в том объеме, который необходим и достаточен для выполнения задачи.
Если блин очередной уровень выкидывает строку типа "FK Violation" — то все, труба. С этой строкой уже нельзя сделать ничего полезного. Информация потеряна. Тот вымышленный пример объекта, который мог бы вылетать из RDBMS заместо строки, можно превратить в съедобное сообщение. Потому, что в нем есть информация. Неважно, где бы происходила эта замена. В сторед процедуре (которая, собсно, является этаким уродливым выростом в сторону middle-tier на теле сервера данных — пожалуйста. Ежели сможете. Потому как не все сервера дают даже там доступ к информации об ошибке, предпочитая отдать на клиента ту самую ненавистную мне строку через голову DB-программера.

И кончается это тем, что разработчики приложений, которые уважают своего пользователя, вынуждены идти на унизительные упражнения типа парсинга текста ошибки в поисках имени таблицы (что приводит к необъяснимым сбоям при апгрейде RDBMC), предварительной проверки всех мыслимых условий (что является избыточным и просаживает перформанс), и так далее.
Не надо идти на поводу у лентяев из Inprise. Не надо считать следующий уровень интеллектуально ограниченным и скрывать от него информацию.
... << RSDN@Home 1.0 beta 6 >>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[10]: Обработка ошибок в многозвенной архитектуре
От: Владик Россия  
Дата: 24.03.03 11:38
Оценка:
Здравствуйте, Sinclair, Вы писали:

В>>Ну и нафига мне на клиенте имя таблицы? И вообще, зачем на клиенте знать что-то о какой-то там базе данных?

S>Ну а нафига тебе на клиенте та строка, которую я привел? А она достаточно типична.

А этой строки не должно быть на клиенте. В крайнем случае на клиенте будет что-то типа: "неизвестное исключение" и кнопка "дополнительно" с таким текстом, который отправляется разработчику. Так вот, и речь как раз о том, что отловить такой эксепшин на уровне СУБД (в хранимой процедуре) и превратить его в "человеческий вид" намного проще, чем формировать определенный тип исключения и потом на клиенте с ним чего-то делать.

В>>Кстати, почему ты СУБД причисляешь к нижнему уровню? Это вполне себе полноценное звено системы, имеющее собственную логику.

S>Потому, что ниже нее уже ничего нету.

А выше что?

S>Это на самом деле неважно. Просто клиент должен показывать что-то типа "Нельзя удалить Склад №2 потому, что на нем числятся товары", а не всякое шаманство. И неважно, откуда приезжает эта строка — из базы данных, из middle tier, или ее клиент формирует. Просто информация о косяке должна передаваться снизу вверх в том объеме, который необходим и достаточен для выполнения задачи.

S>Если блин очередной уровень выкидывает строку типа "FK Violation" — то все, труба.

Видишь ли, проблема в том, что по дефолту именно в таком виде из базы оно и вылетает. И ничего кроме парсинга в таком случае не остается. Что, как ты сам сказал, довольно криво. Так что вопрос только в том, кидать ли из базы "magic code", с последующим формировании на сервере нужного типа исключения, с последующим формированием на клиенте нужного текстового сообщения или сформировать сообщение сразу в базе.

[...]
S>Не надо идти на поводу у лентяев из Inprise. Не надо считать следующий уровень интеллектуально ограниченным и скрывать от него информацию.

Причем здесь Inprise? Если Oracle там и выдает более осмысленную информацию об ошибке, то джавовские драйвера все равно превращают ее в абстрактный SQLException.
Как все запущенно...
Re[3]: Обработка ошибок в многозвенной архитектуре
От: IT Россия linq2db.com
Дата: 24.03.03 18:40
Оценка:
Здравствуйте, Владик, Вы писали:

IT>>А если сервер тебе кидает исключение, которое говорит, что запись в БД не найдена и тебе нужно просто пойти по другой ветке программы без всяких сообщений пользователю?


В>Может, тогда лучше возвращать код ошибки?


Лучше возвращать другой тип исключения (см. второй вариант).

В>[...]

IT>>Самый лучший вариант конечно второй, но он зависит от используемого софта. В принципе, если все вызовы методов проходят через контролируемый шлюз, то в нём можно вполне всё это навернуть.

В>Да, все вызовы проходят через "одно место". Ну почти все.


Тогда в этом месте, если сервер возвращает код ошибки, можно преобразовать этот код в исключение C++ и не мучатся. Механизм исключений куда более гибок и удобен, чем возвращаемые коды ошибок. В частности в исключении можно передать любую информацию, включая вменяемое описание ошибки для конечного пользователя.

Я использую веб-сервисы совместно с MFC. При возникновении исключения на сервере мне приходит SOAP сообщение специального формата. Всё что мне нужно сделать — это преобразовать это сообщение в исключение C++ и протолкнуть его дальше. В результате я имею единообразный и довольно удобный механизм обработки ошибок.
Если нам не помогут, то мы тоже никого не пощадим.
Re[11]: Обработка ошибок в многозвенной архитектуре
От: DarkMAD  
Дата: 29.03.03 19:50
Оценка:
Не знаю как там в Оракле, а в "большом" Sybase Entrerprise, есть чудесная вещь — sp_bindmsg.
На любой constraint (и в том числе foreign key) можно навесить свое сообщение. И все будет по русски.
Вместо constraint violation будет — 'для документа такого -то существует связанный с ним документ сякой-то'
И кроме тривиального login failed ничего не надо обрабатывать. И писать уродские case тоже не надо.

А в SQL Anywhere это unsupported_tsql_feature, блин (
Posted via RSDN NNTP Server 1.5 beta
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.