Мы работаем над различными интеграциями в рой идентично структурированных устаревших баз данных, которые в принципе не могут быть изменены.Для этого мы добавили вспомогательную базу данных для хранения таких вещей, как метаинформация, правила маршрутизации и временное хранение данных для устаревших баз данных.
В основном мы используем NHibernate для подключения к базам данных.Одним из приложений является служба WCF, которая должна вставлять входящие данные в действительно широкие вложенные таблицы (десятки столбцов).Очевидно, что производительность - это проблема, поэтому я рассчитывал быть максимально экономичным с транзакциями NHibernate.В то же время параллелизм оказался проблемой.В процессе производства мы начинали получать зомбированные ошибки транзакций (взаимоблокировки).
Я выполнял балансирование для решения этих двух проблем, но на самом деле не устранил проблемы параллелизма.
Поведение службы настроено на обработку одного запроса за раз, например:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode=ConcurrencyMode.Single]
public class LegacyGateService : ILegacyGateService
Ранее, после некоторого "вдохновения" (читай: копировать / вставить) из Интернета, яВ итоге мы добавили набор классов, называемых что-то вроде XxxNHibernateUtil, для вспомогательной базы данных и устаревших баз данных соответственно.Эти классы управляют сеансами NHibernate и генерируют или повторно используют сеансы из предварительно инициализированных SessionFactories.
Для вспомогательной базы данных это выглядит следующим образом:
public static class LegacyGateNHibernateUtil
{
private static readonly ISessionFactory sessionFactory = BuildSessionFactory();
private static ISessionFactory BuildSessionFactory()
{
try
{
Configuration Cfg = new Configuration();
Cfg.Configure();
Cfg.AddAssembly("LegacyGate.Persistence");
return Cfg.BuildSessionFactory();
}
catch (Exception ex)
{
throw ex;
}
}
public static ISessionFactory GetSessionFactory()
{
return sessionFactory;
}
public static ISession GetCurrentSession()
{
if (!CurrentSessionContext.HasBind(GetSessionFactory()))
CurrentSessionContext.Bind(GetSessionFactory().OpenSession());
return GetSessionFactory().GetCurrentSession();
}
public static void DisposeCurrentSession()
{
ISession currentSession = CurrentSessionContext.Unbind(GetSessionFactory());
if (currentSession != null)
{
if (currentSession.IsOpen)
currentSession.Close();
currentSession.Dispose();
}
}
}
Всякий раз, когда для транзакции требуется сеанс, текущий сеанс ищется и повторно используется в течение срока действия запроса на обслуживание.вызов.Или, по крайней мере: это то, что должно происходить.
РЕДАКТИРОВАТЬ: Контекст сеанса, конечно, устанавливается в hibernate.cfg.xml примерно так:
<property name="current_session_context_class">call</property>
Для устаревших баз данных NHibernateUtil адаптирован дляиметь дело с различными возможными базами данных.Для этого каждое соединение получает свою собственную сборку SessionFactory, которую нужно искать в коллекции Dictionary.В остальном принципы одинаковы.
Тестирование с использованием WCFStorm, кажется, что это нормально работает при отправке одного запроса за раз, но как только я запускаю нагрузочный тест, даже с одним агентом и длинными интервалами, я получаю множество разных типоввсе исключения указывают на одновременные запросы и транзакции, саботирующие друг друга.Я попытался настроить IsolationLevel, но сейчас безрезультатно.
Я думаю, что мне нужно генерировать и обрабатывать сеансы по-другому, чтобы разные транзакции в одной и той же базе данных обрабатывались упорядоченно и не мешали друг другу.Однако мне не хватает понимания того, как сделать эту работу.Любая помощь с благодарностью!
EDIT Для одного метода обслуживания, при тестировании с более чем одним агентом, первый или около того вызов работает нормально, а затем начинает появляться следующая строка исключений, которые относятся только квспомогательная база данных:
- «При преобразовании IDataReader в NDataReader произошла ошибка» / «Недопустимая попытка вызова метаданных при закрытом считывателе.»
- «недопустимый доступ к загрузке коллекции»
- «Начало не удалось с исключением SQL» / «Истекло время ожидания. Период ожидания истек до завершения операции или сервер не отвечает».
- "не удалось выполнить запрос" / "ExecuteReader требует открытого и доступного соединения. Текущее состояние соединения закрыто."
- "не удалось инициализировать коллекцию:" / "Истекло время ожидания.истекло время ожидания до завершения операции, или сервер не отвечает. "
- " Транзакция не была успешно запущена "
- " Транзакция либо не связана с текущим соединением, либо была завершена«.
- "не удалось инициализировать коллекцию:" / "Недопустимая попытка вызова Read, когда читатель закрыт."
Исключение 1, по крайней мере, указывает, что один и тот же сеанс доступен нескольким потокам (возможно, вызовы). Также другие указывают, что текущий сеанс прерывается другими процессами. Но как это может быть, когда я пытался изолировать звонки и поставить их в очередь?
Для другого метода обслуживания эти проблемы не появляются со вспомогательной базой данных, но через некоторое время я начинаю получать исключения (взаимоблокировки) ZombiedTransaction с транзакциями в устаревшие базы данных. Все же ... Что дает?