вторник, 17 февраля 2009 г.

О бюрократии и .NET Remoting

Как и у любой технологии, у .NET Remoting есть свои недостатки, особенно при неправильном использовании.

Систему, которую я сопровождаю, использует всего лишь 50-70 пользователей. Сервер приложений один, failover или load balancing нет, клиентские приложения друг с другом не общаются, все работает в локальной сети... вообщем, мне не очень понятно, зачем вообще разработчики решили использовать Remoting – вроде обычного клиент-сервера бы хватило... Ну, решили, и ладно. Наверное, индусы хотели сделать по-пацански – отделить бизнес-логику от интерфейса. Правда, все равно ни фига не отделили на самом деле, но речь не о том.

Прикол в том, что некоторые серверные объекты кэшируются на клиентской стороне, и если я обновляю сервер приложений, то нужно обязательно обновить и клиенские программы (у всех пользователей)! Иначе они что-то ругаются насчет сериализации. У меня не было времени разобраться - наверное, эту проблему можно решить. Но просто смешно получается – часть бизнес-логики якобы отделена от клиента, но на самом деле клиенты жестко привязаны к серверу, и даже малейшее изменение в серверном коде приводит к ошибке.

А вторая потенциальная проблема с Remoting – это производительность... хотя тут можно поспорить, что вообще-то "все дело в ручках". Итак, вот моя грустная история...


Есть некая форма для конфигурирования системы, она позволяет задавать ключевые даты в производственном цикле (например, "Книга подписана в печать", "Отправлена на склад", "Протестирована на безопасность" и пр.). По идее, пользователи должны регулярно проверять, все ли там настроено правильно. Но то ли кто-то из менеджеров уволился, то ли еще что-то – вообщем, почти год никто ее не контролировал. В результате постепенно накапливалось все больше и больше ошибок в графиках производства соответствующих книг, их приходилось исправлять вручную.

Ну, вроде не беда, потому что форма-то умная. Когда ты исправляешь или вносишь какой-то параметр, то она задним числом пересчитывает все, что нужно. Да только за этот год бездействия уже накопились десятки тысяч несоответствий, и форма просто зависала, не справившись с таким backlog. Мне поставили задачу – улучшить производительность, чтобы пользователи смогли внести изменения за разумное время (не больше нескольких минут ожидания).

Я заглянул в исходники и ужаснулся. Нет, оно вроде как все круто – ООП, паттерны, но для каждой книги делаются десятки вызовов хранимых процедур. Т.е. очень много логики реализовано не на стороне базы данных. Это не проблема, когда мы работаем с одной книгой. Но когда тот же самый код используется для нескольких тысяч книг – вот тут-то начинаются проблемы. Индусы, конечно, об этом не подозревали - ведь когда они разрабатывали систему, база данных была маленькая.

Хотелось все нафиг переписать, но у меня было слишком мало времени. А главное, я не очень понимал, как оно все должно работать. Спецификация написана несколько лет назад, с тех пор систему меняли... Посоветовался со старшим товарищем, и он предложил мне просто кое-что немного оптимизировать, не переделывая по сути. Какая, собственно, разница – ведь мы говорим об одноразовой операции (пересчитать ключевые даты задним числом). А по плану через годик эту систему вообще должны заменить другой, так что нет смысла сильно напрягаться.

Так я и сделал - оптимизировал одну хранимую процедуру, и результат вроде был прекрасный. Производительность выросла в 10-20 раз, я был очень доволен собой. Протестировали, собрали все подписи, внедрили... Менеджер начинает вносить ключевые даты, вроде уже большую часть внес...

И тут у него начинает валить ошибка: Distributed transaction completed. Either enlist this session in a new transaction or the NULL transaction. Какая такая distributed transaction? Моя первая мысль - это что-то связанное с two-phase commit. Но у нас же всего лишь одна база данных... Быстрый поиск в Google подсказал идею: на самом деле сообщение об ошибке не несет в себе полезной информации, .NET Remoting ее скрывает; а истинная причина – таймаут.

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

Так я и сказал начальнице. Но она подняла панику: "Все пропало, ты все сломал, надо откатывать релиз! Я не верю, что это таймаут." Вообщем, я потратил пару дней на отработку других гипотез, но как часто бывает, первая мысль оказалась самой верной. Это действительно был таймаут. Причем он не имел отношения к распределенной транзакции между двумя базами данных – таймаут был прописан для транзакций COM+.

Главная проблема в том, что на тестовом сервере все работает, не удается воспроизвести ошибку. Я специально вставил задержку в свою процедуру... ага, теперь есть точно такая же ошибка, как у пользователя. Сначала я попросил DBA увеличить таймаут в настройках MSDTC. Хм, не помогает, блин – ровно через 5 минут после запуска валится ошибка. Оказалось, индусы использовали атрибут System.EnterpriseServices.TransactionAttrubute. Эти "5 минут" были тупо забиты в тексте программы, а не вынесены в конфигурационный файл.

Что же теперь делать? Увеличить таймаут просто так не могу, потому для этого надо перекомпилировать мой сервер приложений. А если я это сделаю, то перестанут работать все клиенты (смотри выше, клиенты жестко привязаны к определенной версии сервера). А переставить клиентов – это вообще кошмар: для этого нужно снова собирать разрешения со всяких менеджеров, а потом ждать недели три, пока Desktop Team соизволит выполнить мой заказ.

Первая простая идея: на тестовом сервере процедура работает (успевает завершится до окончания таймаута). Значит, вероятно, она будет работать и на рабочем сервере, но только если нет сильной нагрузки. Давайте я просто на выходных (когда никто не работает) подключусь из дома к нашей сети выполню эту операцию?

Начальница категорически говорит "нет": данные принадлежат пользователям, программисты не имеют право их менять самостоятельно, потому что это противоречит Sarbanes-Oxley и т.д. Я все это понимаю, но в данном случае ситуация доведена до абсурда. Фактически нужно ввести одну дату и нажать кнопку "Сохранить", остальное делается автоматически. Какая разница, кто это сделает? Мне не нужно напрягать мозги, что-то там считать... весь вопрос только в том, кто нажимает кнопку "Сохранить".

Ну ладно, нет так нет. Вторая идея: я увеличиваю таймаут, но устанавливаю новую версию программы только на компьютере пользователя (и то временно). Он нажимает кнопку "Сохранить", и я ему ставлю опять старую версию. Этот вариант начальнице тоже не нравится.

Выход у меня остается один – думать, как оптимизировать какую-нибудь хранимую процедуру или создать новый индекс, чтобы уложиться в эти злосчастные 5 минут. С помощью SQL Profiler выяснил, что одного индекса действительно не хватает.

Создал – производительность улучшилось, укладываюсь в 2 минуты. Но достаточно ли этого? Я ведь не знаю, насколько быстро будет на рабочем сервере; может, там будет 10 минут. Кстати, странно – почему на тестовом сервере 2 минуты, а если и база, и сервер приложений на моем компьютере, то всего 30 секунд? У меня что, компьютер быстрее сервера?

Всего запускалось 11 тысяч запросов. Пытаюсь оптимизировать дальше. И вдруг понимаю удивительную вещь: все эти 11 тысяч запросов выполняются всего лишь за несчастных 8 секунд! Остальное время (почти две минуты) уходит на общение между клиентом и сервером приложений через .NET Remoting! Вот это оказалось узким местом. Вот почему на моем компьютере работает в 4 раза быстрее – не нужно слать всякую ерунду по сети.

Т.е. оптимизировать что-то дальше бесполезно. Даже если все 11 тысяч запросов будут выполнятся мгновенно, это мне почти не поможет. Мне остается только создать индекс и надеятся, что этого будет достаточно.

Но не все так просто! Нельзя просто так взять и создать индекс – "пользователи сначала должны его протестировать". Говорю начальнице – как протестировать? Ввести ту самую злосчастную дату на тестовом сервере, нажать "Сохранить" и убедиться, что заработало быстрее? Но ведь я только что сделал то же самое!

Она краснеет, ей тоже понятно, что это идиотизм, и риск при создании нового индекс близок к нулю, и это вопрос скорее к DBA, а не к пользователям... но правила есть правила. Пытаюсь отловить пользователя, что он "протестировал" и дал добро. А он то занят, то снег в Лондоне пошел, и неделю на работу никто не ходил... за эту неделю накопились еще новые несоответствия в базе, и процедура работает еще медленее. К счастью, после долгих мучений мне повезло. Мы уложились в 4 минуты.

Все хорошо, что хорошо кончается. Были написаны сотни емейлов, большие начальники обсуждали все это на совещаниях, а проблема-то не стоила выеденного яйца. Я понимаю – change control очень важен, но ведь есть же еще и здравый смысл! Бывают ситуации, когда нужно чуть-чуть отойти от правил. Я потратил намного больше времени на политику, чем на техническую сторону.

Кроме описанных здесь проблем с .NET Remoting, был еще один интересный момент: за те полтора месяца, которые прошли между тестированием и внедрением системы, появились тысячи новых несоответствий в базе данных. Просто потому, что тестировали мы перед Рождеством, в тихий период. А к концу января пользователи уже навводили кучу новой информации, и это тоже сильно повлияло на произодительность. Так что я извлек для себя важный урок: бывают случаи, когда ценность проведенных тестов резко уменьшается с течением времени.

понедельник, 16 февраля 2009 г.

Неразменный Oyster

Я давным-давно зарегистрировал наши карточки Oyster на сайте TfL и сделал auto-top up. Удобно – деньги за проезд автоматически снимаются с кредитки, не нужно держать в голове, сколько у тебя осталось денег на проездном, и где его можно пополнить (автоматы и кассы есть не на всех станциях).

Но у регистрации есть и другие преимущества. Оказывается, в случае утери или кражи карточки ты можешь не только перенести баланс на другой Oyster, но даже попросить, чтобы его бесплатно прислали по почте. Таким образом, экономишь 3 фунта на покупке новой карточки. Интересно, а если по приколу каждый день заявлять о пропаже? Это ж TfL разорится на почтовых расходах. Надеюсь, они отслеживают таких шутников.

А я эту штуку не знал, недавно пошел покупать запасную карточку (для тещи) прямо в кассу. Кстати, кассир мне сказал: "Ну, удачи тебе!" Наверное, решил, что я только что приехал в Лондон – у большинства сторожилов проездные уже есть.

Не думай о секундах свысока

Я написал маленькую, но полезную хранимую процедуру. Она делает определенные проверки, а потом рассылает по емейлу предупреждения о возможных проблемах. Все проверил – работает отлично. С помощью SQL Jobs настроил, чтобы она запускалась три раза в день.

На следующий день вижу, что что-то не то. Емейлы с предупреждениями были разосланы несколько раз ночью. Зато днем, когда они как раз и нужны, ничего нет. Ну, я сразу стал вспоминать, что последнее время у нас по ночам были каким-то странные проблемы то ли с сетью, то ли SQL Server. Может, как-то сервер перезапустился, а вместе с ним и jobs запустились в неправильное время?

А все оказалось намного проще. Заполняя расписания запуска jobs, я набирал "9 утра" как "09:00" (без секунд). Поэтому все запустилось в первом часу ночи - в 00:09:00, 00:16:30, 00:21:00 и т.д.



Вспомнил другой забавный глюк, связанный со временем. Я разрабатывал банковскую часть система клиент-банк. Клиентская часть была покупная. В ней много разных проблем, а сроки уже поджимают. Мы хотели подключить первого клиента в день рождения управляющего банка – сделать такой своеобразный подарок. Так что сидим допоздна, работаем.

Вроде уже почти готово, и вдруг в какой-то момент моя программа отказывается принимать тестовые платежи от клиентов. Как же так, вчера же работало? Что же я изменил со вчерашнего дня? Пробую и то, и это... перегенерировал ключи ЭЦП, переставил клиентскую программу, взял более старую версию своих исходников... В какой-то момент все снова начинает работать. В чем было дело, я не понял. Ну да ладно. На следующий день повторяется. Опять пробую и так, и эдак, и в какой-то момент снова начинает работать. Опять не понял, в чем дело. На третий день – опять то же самое. Уже и Windows переставляю, даже пробую заменить Millenium на 2000 (или наоборот?), удаляю ICQ. Опять починилось.

Я понял, что надо сделать code freeze. Точнее, full freeze. Вечером полностью протестировал систему. Таким образом, утром я знал, что вчера работало, а за ночь никак не изменились ни программы, ни моя Windows. И все равно утром не работает!

Оказалось, меня подвела излишняя аккуратность. В те "темные годы" обмен информацией осуществлялся, конечно же, с помощью файлов фиксированной длины. Разработчик дал спецификацию, согласно которой я пытался отправлять и принимать разные файлы (платежые поручения, квитанции, запросы, выписки, информационные сообщения). В спецификации описывались допустимые типы данных. Например, числа надо дополнять пробелами слева, а обычный текст – справа; время надо записывать в формате "ччмм", при необходимости дополняя часы и минуты лидирующими нулями. Как выяснилось, в одном месте разработчики нарушили свою же спецификацию – вместо лидирующего нуля использовали лидирующий пробел. Т.е. 9 утра они записывали как " 900", а не как "0900". Моя же программа исправно браковала такие файлы, поскольку в каком-то поле использовался недопустимый символ (пробел). Получается, что каждое утро день в 9:59 проблема еще была, а в 10 она уже исчезала.

понедельник, 9 февраля 2009 г.

Dormant account

Понадобилась мне одна книжка. Но издание редкое, в британских магазинах или на eBay я ее не нашел. Есть она только на сайте американского издательства. А сайт сделан типично по-американски, т.е. вроде как можно выбрать страну, и даже написано, сколько стоит доставка за границу... но в списке стран есть только Америка.

После двух-трех недель переписки я смог получить форму для заказа книги по факсу. Но снять деньги с моей британской карточки они не смогли. Ну, ладно, не беда. У меня есть счет в американском банке, так что я могу послать чек в долларах по почте. Только сначала надо положить денег на американский счет.

Послал свифтовку - она вернулась... причем не полностью, вычли комиссию. Почему вернулись - неясно. Номер счета я точно написал правильно, уже ведь не раз туда слал деньги.

Оказалось, американский банк (Wachovia) перевел мой счет в dormant (дремлющее) состояние, потому что год не было никакой активности. Но что самое интересное: если бы я эту штуку не заметил вовремя, они бы не только закрыли счет, но и перевели бы остаток моих денег в бюджет. А я думал, что в таких случаях банк забирает деньги себе.

четверг, 5 февраля 2009 г.

CheckMyFile

Решил запросить кредитные отчеты через CheckMyFile.com. Вроде это выгодно: они дают информацию сразу из всех трех кредитных бюро (Experian, Equifax, Callcredit). Получается и дешевле, да и удобнее, чем запрашивать по отдельности.

Первые впечатления неважные. По их данным, у нас нет карточки American Express, хотя я точно знаю, что непосредственно у кредитных бюро эта информация есть. Кроме того, они показывают, что у нас было ноль "запросов" за год - это тоже неправда.

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

Но я нашел совсем уж грубую ошибку:

По данным Experian Ваш credit score 568; это выше, чем у 46% жителей UK.
По данным Callcredit Ваш credit score 653; это выше, чем у 43% жителей UK.


Credit score может быть от 0 (наихудший) до 1000 (наилучший). Может, у меня с арифметикой проблемы, но по-моему они перепутали "выше" с "ниже".

Эх, а сайт выглядит вроде вполне красиво и профессионально...

понедельник, 2 февраля 2009 г.

Nikon vs Canon

Мы ходили делать художественное семейное фото. Марина обратила внимание, что фотограф использует Canon. Спросила:

- Почему не Nikon? Что, Canon лучше?
- Что ты! Canon - это мусор, качество отвратительное! Просто он очень надежный, я уже сделал на нем 200 тысяч снимков. Конечно, у Canon тоже есть модели, которые дают отличное качество, но они слишком дорогие.

И еще долго горячий пакистанский фотограф ругал фотоаппарат, которым только что сделал наши недешевые фотографии. Вот британец бы не стал так необдуманно выставлять себя в плохом свете перед клиентами :)

P.S. А вот симпатичные фото ночного Лондона, сделанные с помощью Nikon.

Первый снег

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

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

Кто же те герои, которые все же пришли сегодня на работу? Это украинец (я), южноафриканец, немка, француз и китаец. Британцев пока замечено не было.

Многие на улице одеты не по погоде. Например, полицицейский в рубашке с коротким рукавом. Правда, может в бронижилете не холодно?

Интересно, есть ли в окрестностях Лондона лыжные базы? Сомневаюсь, пока это нерентабельно. Хотя кто его знает, что в будущем нам принесут глобальные изменения климата.

Ratings by outbrain