• Как гарантируется ACID в распределенных транзакциях (по мотивам мутной статьи)?

    @footballer Автор вопроса
    Так судя по комментам в этом топике, двухфазная фиксация или SAGA - это в принципе НЕ ТРАНЗАКЦИИ, т.е. никакой фактической транзакционности там нет? Значит, любая система из связанных микросервисов априори не является надежной системой и всегда возможны потери данных? В любой банковской системе, если она микросервисная (а сейчас все такие), возможны несогласованные данные, и как итог потеря денег со счетов у клиентов из-за ошибок?
    Насчет того, что сбой в сети намного менее вероятен (да и не только в сети, а где-нибудь даже на сервере), чем в СУБД, я не особо согласен, вообще часто бывает ситуация даже когда в браузере заходишь на сайт, а браузер выдает какую-нибудь ошибку сети, но стоит перезагрузить страницу, как сайтик загружается.
    Ответ написан
    Комментировать
  • Почему то, что считается преимуществом классов над структурами, иногда выставляется недостатками?

    @footballer Автор вопроса
    когда размер ссылки сравним с размером данных (как раз случай кортежей)

    но я могу сделать кортеж, который будет состоять не из ссылок, а из структур.
    struct VeryBigStructure{
        // тут сотни жирных полей-структур
        VeryBigStructure one;
        ...    
        VeryBigStructure oneHundred;
        public static (VeryBigStructure fitst, VeryBigStructure second) GetTwoVeryBigDataObjects()
        {
            var res = (first: new VeryBigStructure(), second: new VeryBigStructure());
            return res;
        }
    }

    В этом случае придется копировать и возвращать 2 гигантских объекта, а если цепочка вызовов и возвратов большая, то делать это придется несколько раз.
    А в случае, если бы я мог вернуть кортеж-класс, то оба гигантских объекта бы забоксились в кучу, а возвращалась бы только ссылка на них. Это же лучше, чем по несколько раз копировать одни и те же гигантские объекты из одного стека в другой?

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

    Ну вот, надо использовать классы, но удобно вернуть через кортеж не получится, придется шлепать класс-обертку. Хотя можно было бы просто разрешить возвращать анонимные объекты.
    Ответ написан
  • Обясните по коннекшен пулу и транзакциям в EF и SQL Server - почему так написано по ссылкам?

    @footballer Автор вопроса
    Если SPID разные то и сессии разные.

    Сессии разные - значит, коннекшены тоже разные, сессия одна - коннекшен один, так же?
    Мне передали прогу на EntityFramework, внутри нее подряд несколько раз создается new DbContext. Каждый DbContext обернут в свою собственую TransactionScope. Т.к. все DbContext представляют одну логич. операцию, то сейчас реализовано коряво, и мне нужно убрать все индивидуальные транзакции у каждого DbContext и обернуть их всех в 1 транзакцию. Чтобы много не переделывать, я пока решил не заменять создание нескольких DbContext на 1, а просто обернул все DbContext внутрь одного TransactionScope. У первого DbContext я удалил его собственную транзакцию, у второго забыл удалить (получилось, что у меня в проге стало 2 транзакции - внешняя и вложенная). Получился сокращенно такой код:
    using (var transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions {IsolationLevel = IsolationLevel.Snapshot})) 
    {
       var data1Id;
       using (var dbContext = new DB()) 
       {
    	   var data1= new Data1();
    	   dbContext.Table1.Add(data1);
    	   /*1*/dbContext.SaveChanges();
    	   data1Id = data1.Id;
       }
       if (data1Id != 0)
       {
    		using (var transactionScope2 = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions {IsolationLevel = IsolationLevel.Snapshot})) 
    		{
    			using (var dbContext = new DB()) 
    			{
    				dbContext.Table2.Add(new Data2() { Data1Id = data1Id, ... });
    				 /*2*/dbContext.SaveChanges();
    				transactionScope2.Complete();
    			}
    		}
       }
       transactionScope.Complete();
    }

    Запустил прогу, первый DbContext выполнил запросы внутри внешней транзакции (произошел инсерт, но пока без коммита), второй DbContext начал выполнять запросы внутри своей вложенной транзакции. В итоге второй DbContext простоял в дебаге полминуты, а потом кинулся эксепшен что-то типа "ожидание времени окончания операции истекло", все транзакции роллбэкнулись. Я посмотрел в SQL Server Profiler - запросы из разных DbContext имели разный SPID. В итоге я сделал себе такой вывод - т.к. SPID разные, то и коннекшены были разные, значит, sql сервер воспринимал запросы от разных DbContext как параллельные (каждый в своих собственных транзакциях), хотя по логике проги это должны были быть взаимозависимые последовательные запросы внутри одной транзакции. В итоге произошла взаимная блокировка (кстати, я не понял, почему, т.к. разные DbContext инсертили в разные таблицы), и транзакция откатилась (или какая там ошибка могла произойти?).
    Получается, что транзакция откатилась, потому что 2 разных DbContext имели разные транзакции и разные сессии (коннекшены). Если бы был 1 общий DbContext (соотвественно, 1 общий коннекшен), то транзакция бы выполнилась успешно, как я понимаю.
    Я начал гуглить и нашел те ссылки. Если я их правильно понял, в них говорится, что не факт, что 2 разных DbContext будут иметь разные коннекшены, потому что "они шарят общую строку подключения, и поэтому коннекшен для второго DbContext может взяться тот же самый, что и для первого DbContext, потому что коннекшен первого DbContext сохранится в пуле коннекшенов даже после закрытия первого DbContext". Вот это меня и напрягло и заставило тут задать вопрос. Потому что это же получается, что если у меня многопользовательская система и разные контроллеры обрабатывают независимые запросы разных юзеров в разных потоках, и каждый контроллер, естественно, создает свой new DbContext , то даже если я оберну весь код в транзакции, то все-равно строка подключения к БД одинаковая у всех DbContext, значит, они могут юзать один-единственный коннекшен из пула коннекшенов, это значит, что PSID будет один и тот же, и sql сервер будет выполнять их в общей транзакции, а не в индивидуальных. Но такого же не может быть, иначе бы просто не было смысла открывать транзакции.
    Кто-нибудь может это объяснить?
    Еще то, что я не понял:
    1) в коде выше после прохождения точки /*1*/dbContext.SaveChanges(); профайлер пишет "BEGIN TRANSACTION" и PSID = 53 (все вроде бы правильно, транзакция началась во время первого инсерта), а в точке /*2*/dbContext.SaveChanges(); профайлер пишет "BEGIN TRANSACTION" и PSID = 59 (но тут я не понял, транзакция второго DbContext же вложенная, но в новой сессии с PSID = 59 открывается только один "BEGIN TRANSACTION". Это значит, что первая (внешняя) транзакция теряется для всех последующих открытых сессий после того, как первая сессия "захватила" во время выполнения инсерта эту транзакцию, несмотря на то, что все последующие сессии в коде открываются внутри этой внешней транзакции? Или как? Это значит, что вторая сессия выполнялась только внутри одной транзакции, хотя в коде она вложена в 2 транзакции?).
    2) уровень изоляции задан как snaphot, но это никак не отображается в профайлере. Профайлер показывает только строку "BEGIN TRANSACTION". Я хочу убедиться, что на sql сервер правильно передается уровень изоляции, но не могу, т.к. не вижу этого в профайлере. Возможно, нужно включить больше чекбоксов во вкладке "выбор событий", но я не могу найти по названию событий, какой чекбокс нужно для этого включить. В общем, подскажите, что сделать, чтобы профайлер показывал уровень изоляции.
    Ответ написан
  • LINQ в С#: что происходит с Func, который вложен в Expression?

    @footballer Автор вопроса
    Как я понял, данная проблема невозможности выноса вложенной лямбды в отдельную переменную вызвана тупостью сишарпового компилятора? Т.к. он мог бы во время компиляции распознать, что мы в Any передаем переменную типа Func, и вместо экспрешшена со ссылкой на переменную сгенерить экспрешшен с вложенной лямблой, которую бы он прочитал из переменной во время компиляции? Но он вместо этого создает экспрешшен со ссылкой на переменную, из-за чего парсер падает?
    Ответ написан
    Комментировать