Archive for the ‘Uncategorized’ Category

Индокод, или почему я не люблю Индо-программистов.

Thursday, February 26th, 2009

Вдохновила меня на этот пост интересная история, произошедшая на днях. В публичном чате появилась ссылка на Саммит индийских девелоперов – http://www.developersummit.com/. В общем-то событие рядовое, и ничем особо не примечательное, если бы не язвительное упоминание про PHP ошибку на сайте:

Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in /home/develop/public_html/classes/ApplicationManager.obj on line 102 …..

В общем даже в этом нет ничего страшного, ну ошибка и ошибка с кем не бывает, хотя на Production системах ошибки необходимо в принципе отключать, ну да ладно. Главное другое, код ошибки может много сказать знающему человеку. Что же мы имеем в данном случае – “/home/develop/public_html/classes/ApplicationManager.obj”.

Путь похож на cPanel систему, с PHP-файлами имеющими нестандартное расширение, и как следствие полный доступ через Web.

Дальше дело техники, открываем:

http://www.developersummit.com/classes/ApplicationManager.obj.

<?php
/**
****************************************************************************
*
* Copyright             :   {{CENSORED}} © 2008-2009 by
*                           {{CENSORED}} Technologies® Private Limited.
* Description           :
* File-ID               :
* Author                :   {{CENSORED}}
* Date of Creation      :   28-March-2008
* Date of Modification  :
* Modified by           :
* Modification          :
*
****************************************************************************
*/

require_once(’./lib/SQL_Setup.obj’);

Там имеется ссылочка require_once(’./lib/SQL_Setup.obj’);

Открываем ссылку http://www.developersummit.com/lib/SQL_Setup.obj – вуаля: пароли, явки (строка подключения к базе и тд).

Офис лежал пол часа …

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

Установка Eclipse на Linux

Monday, June 2nd, 2008

За последнее время минимум 4 человека столкнулись с проблемой установки Eclipse в Linux. Помогать всем в онлайне у меня больше нет сил, поэтому появилась идея написать этот пост.

Итак, приступим.

Установка Eclipse на Linux 32bit.

Установка на 64 битные версии Linux, это отдельный разговор и пока мы ее рассматривать не будем.

1. Театр начинается с вешалки, а Eclipse с Java. Поэтому первое что мы делаем, качаем JDK(или JRE). Для этого открываем ссылку: http://java.sun.com/javase/downloads/index.jsp и качаем.
Например первая ссылка – Java SE 6 Update 10 Beta. Качать рекомендую bin файл – это самораспаковывающийся архив, его можно распаковать куда угодно и настроить под него все, что нужно
2. После этого качаем Эклипс. Я предпочитаю PDT, но вы можете выбрать любой билд который вас устраивает. Идем по ссылке http://download.eclipse.org/tools/pdt/downloads/release.php?release=R20080103 и качаем полный билд для Linux.

Предположим, что для скачивания вы выбрали папку ~/Download. Это не принципиально, но так будет проще объяснять.

Итак, у нас все скачалось, переходим к Терминальным упражнениям.

1. Открываем терминал и запускаем bin файл с Java. Для этого наберем команду:
/bin/bash ~/Download/jdk-6u10-linux-i586.bin
Один раз необходимо будет сказать yes, что вы ознакомились с лицензионным соглашением.
После этого Java будет разархивирована в папку ~/Download/jdk1.6.0_10, если вы думаете, что это все, то спешу вас огорчить, веселье только начинается.
Рекомендую ставить Java в папку /usr/java, с возможностью иметь несколько версий jdk. Для этого создаем папку /usr/java:
mkdir /usr/java
Копируем в нее папку с JDK(JRE):
cp ~/Download/jdk1.6.0_10 /usr/java
Заходим в папку /usr/java:
cd /usr/java
Создаем в папке /usr/java 2 символьческих ссылки на Java: default и latest -> ./jdk1.6.0_10/
ln -s ./jdk1.6.0_10/ ./latest
ln -s ./jdk1.6.0_10/ ./default
Теперь настраиваем работу интерпретатора Java и ссылки на него, открываем /etc/bashrc (или ~/.bashrc) и добавляем в конец файла строки:
### Exporting Environment Variables ###
export JAVA="/usr/java/latest/bin/java"
export JAVA_HOME="/usr/java/latest"
export PATH="${JAVA_HOME}/bin:${PATH}"
export CLASSPATH="${JAVA_HOME}/lib:${JAVA_HOME}/jre/lib"

Теперь открываем новое терминальное окно, это необходимо для того, чтобы использовался обновленный bashrc, и выполнфем команду:
java -version

Если в результате имеем на выходе, подобрые строки:
java version "1.6.0_10"
Java(TM) SE Runtime Environment (build 1.6.0_10-b13)
Java HotSpot(TM) Server VM (build 10.0-b19, mixed mode)

начинаем бить в бубен – Java готова к работе.

Если необходимо использовать java для обработки апплетов в firefox, идем в папку /home/{yourUserName}/.mozilla/plugins в этой папке, делаем символическую ссылку: libjavaplugin_oji.so на файл:
/usr/java/latest/jre/plugin/i386/ns7/libjavaplugin_oji.so (для JDK) или
/usr/java/latest/plugin/i386/ns7/libjavaplugin_oji.so (для JRE)
Все – после перезапуска ваш файерфокс будет любить Java-апплеты.

2. Приступаем к Eclipse.
Eclipse нужно разархивировать в папку, в которую сможет писать пользователь, из-под которого Eclipse будет запускаться, например в /usr/local/eclipse.
Если вы выбрали папку /usr/local/eclipse, не забудьте сделать вашего пользователя владельцем этой папки:
chown -R user:user /usr/local/eclipse
После этого можем запускать Eclipse, для начала рекомендую запускать его из коммандной строки с флагом -clean
/usr/local/eclipse/eclipse -clean
Перед запуском Eclipse рекомендую поправить конфигурационный файл Eclipse – /usr/local/eclipse.pdt/eclipse.ini, господа не пожалейте памяти для бедного калеки, рекомендую поставить следующие опции:
-vmargs
-Xms256m
-Xmx512m

Если хотите использовать определенную версию Java для вашего Eclipse, добавьте в файл /usr/local/eclipse/eclipse.ini путь к java:
-vm
/usr/java/latest/bin/java

То же самое можно сделать, запустив Eclipse с параметром -vm:
/usr/local/eclipse/eclipse -vm /usr/java/latest/bin/java
Ну или для гурманов, запускаем Eclipse прямо через виртуальную машину:
/usr/java/latest/bin/java -jar /usr/local/eclipse/plugins/org.eclipse.equinox.launcher_1.0.1.R33x_v20070828.jar

Вуаля, спасибо за внимание, вопросы приветствуются.

MySQL-тюнинг. Настраиваем по-взрослому.

Friday, May 30th, 2008

Итак, для начала благодарности. Выражается нереальная благодарность Олегу Копачовцу(он же Dr. Cop, http://www.kopachovets.com), за собранный материал и анализ фактов, а также за удачную подачу материала.
Идея написания статьи витала в воздухе уже давно, вопрос правильной настройки сего загадочного зверька всегда вызывал у меня интерес. Мало кто знает, но правильно оттюненный MySQL может работать в 10-100 раз быстрее своего неоптимизированного собрата из базовой установки.
Я человек не жадный, именно поэтому данная статья увидела свет. Итак, приступимс …

Поучение 1. Вы еще не стартовали MySQL? – Тогда мы идем к вам …

Если вы думаете, что вы стартовали демона MySQL и на этом ваша работа закончилась – вы оптимист.
А уверены ли вы, что стартовали MySQL правильно? Да? Ну так давайте посмотрим, что стоило бы сделать в первую очередь …

Итак, предположим, что MySQL стартуется из init-скрипта, через демон mysqld_safe. Находим строку запуска MySQL, и что мы видим?

$bindir/mysqld_safe --datadir=$datadir --pid-file=$pid_file >/dev/null 2>&1 &

И чего? – спросите вы.

А вот чего, аккуратно добавляем к строке запуска следующие параметры:

–skip-name-resolve – Не производится разрешения имен хостов. Все значения в столбце Host в таблицах привилегий должны быть IP-адресами или значениями localhost
Это сильно увеличивая быстордействие запросов за счет выключения постоянных DNS запросов (до 1000% при “внешних” соединениях с mysql)
–skip-locking – Нужно запускать mysqld с опцией –skip-locking. Запрет внешней блокировки существенно повысит скорость работы. Редко когда с одной базой работают одновременно 2 сервера
Ограничение: при запрете внешней блокировки нельзя будет использовать несколько серверов для работы с теми же базами данных.
–low-priority-updates – INSERT/UPDATE в БД являются более низкоприоритетными, чем SELECT… Думаю для многих проектов это будет актуально, хотя пользоваться этим надо с умом.

$bindir/mysqld_safe --datadir=$datadir --pid-file=$pid_file --skip-name-resolve --low-priority-updates --skip-locking >/dev/null 2>&1 &

Поучение 2. Ну вроде бы запустились …

Мы конечно уже обрадовались, думается, ну вот же он – наш Database server, но-но, не тут то было, а как же my.cnf?
Открываем /etc/my.cnf, и начинаем стучать в бубен (то есть определять конфигурационные переменные MySQL):

thread_concurrency. Если у вас много памяти и много таблиц, то для увеличения производительности, при запуске сервера рекомендуется использовать следующие формулы, учитывающие специфику работы mysql под различные ОС:

  • Для FreeBSD: thread_concurrency = (кол-во процессоров)*(кол-во ядер в одном процессоре)
  • Для Linux: thread_concurrency = (кол-во процессоров)*(кол-во ядер в одном процессоре)*3

Почему на Linux можно давать можно и нужно давать больше потоков, чем на FreeBSD? Это связано с тем, что Linux умеет распределять и управлять потоками между ядрами, а FreeBSD не умеет, зато FreeBSD умеет эффективно распределять процессы, чего, в свою очередь, не умеет Linux.

max_connections=4000 Разрешенное количество одновременно подсоединенных клиентов. Ставим 4000, чтобы не было казусов с “Too many connections…”, но стараемся поставить все таки меньше, так как, Mysql 5.0 все ещё использует select() вызов, а если хотим держать большое число соединений, то необходимо ставить mysql 6.0, который построен на libevent (epoll).
Примечание: если вам хочется использовать больше 4000 коннектов к базе, вынужден вас огорчить, с этим есть проблема. Задать такое количество соединений, вы конечно сможете, но стандартные билды не позволяют использовать такое количество соединений. Вы конечно, можете скачать патчи, для повышения количества соединений, – но это не спасет Отца Русской демократии, даже в этом случае вы будете иметь ограничение порядка 7000 коннектов.

key_buffer=1024M Блоки индексов буферизированы и доступ к ним разрешен всем потокам. key_buffer – размер буфера, используемого для блоков индексов. Чтобы улучшить обработку индексов (для всех операций чтения и записи нескольких элементов), необходимо увеличить это значение настолько, насколько возможно. Рекомендуется выставлять это значение от 15% до 25% ОЗУ, чтобы система не начала сохранять временные файлы на диске, что значительно снизит производительность.
Производительность буфера ключей можно проверить, выполнив команду show status LIKE "Key%"; и проверив значения переменных Key_read_requests, Key_reads, Key_write_requests и Key_writes. Отношение значений Key_reads/Key_read_request обычно должно быть < 0,01.
Пример:


mysql> show status LIKE "Key%";
+------------------------+------------+
| Variable_name | Value |
+------------------------+------------+
| Key_blocks_not_flushed | 0 |
| Key_blocks_unused | 818569 |
| Key_blocks_used | 287552 |
| Key_read_requests | 3012333357 |
| Key_reads | 2435564 |
| Key_write_requests | 668018001 |
| Key_writes | 70927911 |
+------------------------+------------+

Заметка: Key_buffer является общим для всех потоков, все остальные буфера выделяются для каждого потока конкретно

key_buffer = 0,25 * Объему ОЗУ - поскольку у меня 4Gb оперативной памяти, мне не жалко отдать 1Gb для индексов.

table_cache=1024 – Количество открытых таблиц для всех потоков. С увеличением этого значения увеличивается количество дескрипторов файлов, необходимых для mysqld. Чтобы узнать, необходимо ли изменять значение кэша таблиц, следует проверить значение переменной Opened_tables в вашем сервере MySQL.


mysql> show status LIKE "Opened_tables%";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Opened_tables | 685 |
+---------------+-------+

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

sort_buffer=128M – Каждый поток, которому необходимо произвести сортировку, выделяет буфер данного размера. Увеличение данного значения позволит ускорить выполнение операторов ORDER BY или GROUP BY. “Увлекаться” большим значением не стоит, а посчитать его можно исходя из среднего значения открытых потоков (Threads_running) и кол-ва ОЗУ сервера.


mysql> show status LIKE "Threads_running%";
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| Threads_running | 4 |
+-----------------+-------+

Текущее значение Threads_running равно 4, таким образом на буферы сортировки у нас выделяется в среднем 512Mb.

record_buffer=32M – Каждый поток, осуществляющий последовательное сканирование, выделяет буфер указанного размера для каждой сканируемой таблицы. Если проводится много последовательных операций сканирования, это значение можно увеличить. Адекватно оценить/подсчитать размер этого буфера можно исходя из данных о количестве прочитанных строк из таблиц mysql и объема данных в таблицах… Обычно рекомендуется принять его в 4-6 раз меньшим чем sort_buffer.

query_cache_limit=2M – Результаты, превышающие это значение, не кэшируются (по умолчанию – 1Мб). Зависит от типа извлекаемых данных из mysql. Если запросов много и в то же время преимущественно извлекается небольшое количество данных (1 Mb), то данное значение лучше уменьшить.

max_join_size=1000000 Это защита от кривых рук программиста, способного join`ом на 10 миллионов записей похоронить даже 4-х процессорный сервер.
Объединения, которые потенциально могут считывать более max_join_size записей, будут возвращать ошибку. Это значение нужно задавать, если ваши пользователи осуществляют объединения, которым недостает оператора WHERE, – такие объединения занимают много времени, а затем возвращают миллионы строк.

max_sort_length=20 – Защита от кривых мозгов архитектора БД, когда не стоят адекватные лимиты по индексам сортировки текстовых полей
Параметр определяет, сколько байтов следует использовать при сортировке значений BLOB или TEXT (обрабатываются только первые max_sort_length байтов каждого значения, остальные игнорируются).

thread_cache_size=64 Определяет, сколько потоков должно сохраняться в кэше для повторного использования. После отключения клиента потоки клиента помещаются в кэш, если там не больше потоков, чем thread_cache_size. Все новые потоки сначала берутся из кэша, и только когда кэш становится пустым, создаются новые потоки. Значение этой переменной можно увеличить, чтобы повысить производительность, если создается много новых соединений (если потоки у вас хорошо организованы, обычно заметного улучшения производительности не наблюдается). Насколько эффективен текущий кэш потоков, можно определить по разнице между Connections и Threads_created. Если есть возможность, рекомендуется установить это значение не меньше, чем значение переменной Max_used_connections. Если значение этой переменной больше 128, рекомендуется ограничиться этим значением.

mysql> show status LIKE "Max_used_connections%";
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| Max_used_connections | 62 |
+----------------------+-------+

В нашем случае, Max_used_connections = 62, поэтому установим этот параметр в 64.

myisam_sort_buffer_size=512М – Буфер, который выделяется для сортировки индексов при выполнении команды REPAIR или для создания индексов при помощи команд CREATE INDEX или ALTER TABLE. Рекомендуется не жадничать …

net_read_timeout=12 – Количество времени в секундах, на протяжении которого ожидаются дополнительные данные от соединения, пока не будет отменено чтение. Обратите внимание, что мы не ожидаем поступления данных от соединения, время ожидания определяется по write_timeout.

net_write_timeout=15 – Время ожидания записи блока через соединение, пока запись не будет прервана (в секундах).

wait_timeout=30 – Время в секундах, на протяжении которого сервер ожидает активности соединения прежде, чем закрыть его.
Примечание: мы, предполагаем, что наша система очень динамична, и висеть конекшенам по несколько часов не требуется, 30 секунд достаточно даже для очень медленных запросах от Web-приложения.

interactive_timeout=600 – Количество времени в секундах, на протяжении которого сервер ожидает активности со стороны интерактивного соединения, прежде чем закрыть его. Интерактивный клиент – это клиент, который использует параметр CLIENT_INTERACTIVE для mysql_real_connect(). См. также информацию по wait_timeout.

long_query_time=30 – Если обработка запроса отнимает больше указанного промежутка времени (в секундах), значение счетчика Slow_queries будет увеличено. Если используется параметр –log-slow-queries, запрос будет записан в журнал медленных запросов.
Значение этого параметра должно быть примерно равно time_limit скрипта php или временно лимиту операции выдачи, т.к. часто получаются ситуации когда PHP-скрипт уже вылетел по time_limit, а бендненькое умирающее животное MySQL все еще корчится в конвульсиях над запросом по группировке 10 млн записей.

Поучение 3. А не выпить ли нам таблеток от Склероза?

Кэш в MySQL называется QuickCache или он же QCache. Ошибочно мнение, что эффективность использования кэша это отношение хитов в кэш к инсертам в кэш. Все немного сложнее. Эффективность попадания в кэш можно оценить вот таким показателем:

qcache_hit_ratio = qcache_hits / (qcache_hits + qcache_inserts + qcache_not_cached)

Посмотреть эти значения можно следующим образом:


mysql> SHOW STATUS LIKE 'Qcache%';
+-------------------------+--------+
| Variable_name | Value |
+-------------------------+--------+
| Qcache_free_blocks | 36 |
| Qcache_free_memory | 138488 |
| Qcache_hits | 79570 |
| Qcache_inserts | 27087 |
| Qcache_lowmem_prunes | 3114 |
| Qcache_not_cached | 22989 |
| Qcache_queries_in_cache | 415 |
| Qcache_total_blocks | 912 |
+-------------------------+--------+

Если это значение > 0.8, то значит 80% ваших запросов попадают в кэш, это очень хороший показатель.
Если % попадания в кэш низкий, то необходимо увеличить значение query_cache_size.
Текущее значиние можно посмотреть так:

SHOW VARIABLES LIKE 'query_cache_size';

Опять же возникает вопрос: как выбрать адекватное значение query_cache_size?
В этом поможет Qcache_lowmem_prunes. В этой переменной хранится число запросов, которые были убраны из кэша из-за необходимости кэширования новых запросов. Необходимо стремится к такому размеру кэша, при котором Qcache_lowmem_prunes будет лишь незначительно увеличиваться. Для этого, рекомендуется сравнить разницу значений Qcache_lowmem_prunes за час и кол-во запросов, поступивших на mysql за этот же час.
На практике, для расчета query_cache_size можно использовать одну из 2-х формул:


query_cache_size = (число запросов за 10 минут)*(срений объем ответа на запрос) * 1,2
или
query_cache_size = (объем трафика за 10 минут) * 1,2

Это позволит закэшировать запросы на 10 минут + дать дополнительные 20% памяти на фрагментацию кэша и дополнительный резерв кэширования
Подсчитать количество и средний объем ответа на запроса можно использую переменные Bytes_sent соответственно

И так значения query_cache_size мы увеличили, после чего стоит обратить внимание на значения Qcache_total_blocks, Qcache_free_blocks и Qcache_queries_in_cache. MySQL хранит кэш в блоках. На 1 запрос необходимо 2 блока: один для самого текста запроса, второй для результата.
Если рассмотреть таблицу со значения Qcache%
Общее количество блоков кэша Qcache_total_blocks – 912
Закешировано сейчас 415 запрос, а значит занят 415*2 = 830 блоков
свободно блоков Qcache_free_blocks – 36. Чем больше незадействованных Qcache_free_blocks, тем больше степень “фрагментации” кэша.
Если большинство запросов имеют небольшой объем результирующих данных, то стоит уменьшить минимальный размер блока кэша query_cache_min_res_unit, который по умолчанию равен 4 Кб.
Если же большинство запросов возвращают много данных – то стоит увеличить размер блока кэша.
Главное – это добится минимального значения Qcache_free_blocks.

Если же случилось непоправимое, и ваш кеш вас подводит, рекомендуется не забыть про следующие команды MySQL:
FLUSH QUERY CACHE – дефрагментировать кэш
RESET QUERY CACHE – очистить кэш

Поучение 4. СМЕРШ. Ищем злодеев и вредителей.

Без комментариев:

–log-slow-queries=/var/log/slow_queries

Перед этим, стоит помочь MySQL (на всякий случай):

#touch /var/log/slow_queries
#chmod 777 /var/log/slow_queries

Рекомендуется ежедневно смотреть лог медленных запросов через less или же пользоваться mysqldumpslow.

Примечание: –log-long-format позволяет заносить в лог запросы, не использующие индексов. Это также полезная информация, для поиска внутренних врагов.

Поучение 5. Подсматриваем и вынюхиваем.

Даже если ваши руки настроили MySQL как конфетку, НЕ ЗАБЫВАЙТЕ О МОНИТОРИНГОВОМ программном обеспечении, – целее будете …

Поучение 6. Что делать и Кто виноват?

И так в один прекрасный момент мы видим, что очень медленно открываются страницы нашего драгоценного сайта…
При выяснении причин и при просмотри top сервера обнаруживаем, что паршивой овцой в нашей боевой linux-связке оказался таки MySQL. Главное спокойно и без паники.

Админы с поверхностным представлением об MySQL сделают “service mysql restart” и на этом успокоятся… Вот только, минут через 10-15, как правило, MySQL опять начнет бится в конвульсиях.

Админы с более продвинутой встроенной логикой начнут делать mysqladmin processlist и пытатся выловить кто же мучает их MySQL и найти причину. Не спорю метод эффективен, но может оказаться что сайт развился до такой степени что ему уже просто не хватает MySQL ресурсов.

Итак, что же мы будем делать в сутации когда наш верный конь MySQL захромал?

  • таки да… “show processlist;” – и попытаться найти первопричину загрузки. По возможности ослабить её или устранить. Возможно какие-то боты “долбят” ваш сервер
  • если таки причина не устранена пробуем сделать: mysqladmin flash-tables . Такое действие поможет закрыть устаревшие дескрипторы для таблиц и таким образом можно вправить МОСК своему любимому MySQL
  • FLUSH QUERY CACHE – дефрагментировать кэш
  • service mysql restart

Если ничего из вышеперечисленного не поможет, рекомендую пинать Системного Архитектора на тему построения более Масштабируемого решения. :)

Чего желаемс …

Wednesday, March 19th, 2008

Итак, топ 3 самых ожидаемых новинок ближайших месяцев.

Top 1. На первом месте идет релиз PHP 5.3.0. Чего ждем-то, ответ однозначный – пространств имен. Если чесно уже месяцев 7 как в билдах 6-го php они доступны для прощупывания, и я уже успел с ними поиграться, но хочется уже узаконить наши отношения :) . По моим прогнозам релиз версии 5.3.0 придется на конец апреля, начало мая. Так что, ждать уже не долго.

Top 2. Немного огорчает отсутствие IDE, которое бы поддерживало пространства имен в PHP, но все-таки не очень сильно, Zend все-таки радует. Итак, открываем планы разработки PDT – http://www.eclipse.org/pdt/plans/php_plan_1_1.php, смотрим планы разработки второго milestone (дата выхода 16 июня 2008 года) и радуемся:

Milestone 2:

  • PHP 5.3 Namespace Implications on PHP Model
  • Bug Squash (List of bugs can be found here)
  • Code Templates
  • Code Assist for Dynamic Variables

Top 3. Не знаю кто как, а я уже успел прощупать KDE 4. Впечатление очень двоякое. Идея просто супер, скорость не сравнима с версиями 3.5.* (Qt 4 понимаешь ли), но общее состояние разработки оставляет слишком гнетущее впечатление, ребята явно поспешили с релизом. Очень мало нормальных аплетов и параметров настройки системы реализовано. Итак ждем августа, возможно летний релиз сделает систему пригодной к стабильной работе.

Благодарности и прочее …

Tuesday, March 18th, 2008

Итак начнем …

Для начала хочется выразить благодарность Reijii (Дмитрий Кочетов – reijii.solartxit.com), за то что пинал. И Михаилу Клишину (novemberain.com) – за воодушевляющий пример.

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

Faithfully Yours,
Eugene A. Kalosha