Вы находитесь на страницах старой версии сайта.
Переходите на новую версию Interface.Ru

ОПИСАНИЕ
03.04.03


RDM Embedded 6.0

 

Резервирование данных

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

Система зеркального отображения данных REMS (RDM Embedded Mirroring System) предоставляет интегрированное решение на уровне ядра СУБД для ведения единственной зеркальной копии основной базы данных. Тип носителя основных и зеркальных данных не важен, если этот носитель имеет интерфейс файловой системы. Например, основная база данных может храниться в оперативной памяти, а зеркальная – в энергонезависимой флэш-памяти. В этом случае высокое быстродействие прямого доступа к памяти сочетается с возможностью надежного хранения данных. Другим примером может быть хранение основной и зеркальной базы данных на разных жестких дисках. При повреждении основной базы данных или ошибке жесткого диска, на котором она находится, зеркальная база данных позволит выполнить восстановление информации.

Система REMS ДАЕТ возможность создания отказоустойчивых приложений, однако само по себе ядро СУБД не обеспечивает эту отказоустойчивость. Используя заложенные в СУБД функции избыточности данных, разработчик должен сам реализовать уровень отказоустойчивости приложений, необходимый для каждого конкретного случая. Если основная база данных становится недоступной, разработчик может воспользоваться функциями системы зеркального копирования для восстановления основной БД или для доступа к зеркальной базе. Система REMS состоит из двух основных компонентов. Первый компонент аналогичен подсистеме ведения журналов транзакций исполняемой библиотеки RDM Embedded и создает зеркальные журналы с двоичными копиями всех изменений основной базы данных, происшедших в течение одной транзакции. На основе этих зеркальных журналов второй компонент выполняет соответствующие изменения в зеркальной копии базы данных. Это может выполняться либо синхронно, либо асинхронно. Наибольшее быстродействие достигается при асинхронном зеркальном копировании.

Важной частью схемы синхронизации является реализация обмена и управления данными между этими двумя компонентами системы зеркального отображения данных. Механизмы синхронизации и обмена данными между компонентами основаны на характерных для операционных систем элементах обмена данными между процессами и внутри процессов: очередях сообщений, мьютексах, семафорах и событиях. Начальная реализация этой системы избыточности данных требует, чтобы приложение RDM Embedded и система зеркального копирования REMS работали на одном компьютере или устройстве. Зеркальные копии диска удаленного компьютера можно вести на подключенных или смонтированных через NFS сетевых дисках, но приложение и система зеркального отображения должны выполняться на одной машине.

Интерфейс SQL

К новшествам RDM Embedded 6.0 относится также замена dbQuery полным интерфейсом SQL. Этот интерфейс позволяет выполнять запросы и обновлять базы данных RDM с помощью стандартных команд языка SQL, подаваемых в ядро через интерфейс, совместимый с ODBC 3.5. В будущем планируется включить в RDM Embedded полный драйвер ODBC и интерфейс OleDB.

Приведенные ниже интерфейсы API являются подмножеством стандарта ODBC 3.51, наиболее полно отражающего потребности рынка операционных систем реального времени.

API
ODBC 3.51
Интерфейс SQL в RDM Embedded
SQLBindCol C V
SQLConnect C V
SQLDescribeCol C V
SQLDisconnect C V
SQLExecDirect C V
SQLExecute C V
SQLFetch C V
SQLGetCursorName C V
SQLNumResultCols C V
SQLPrepare C V
SQLRowCount C V
SQLSetCursorName C V
SQLNumParams C V
SQLBindParameter C V
SQLAllocHandle C V
SQLCloseCursor C V
SQLEndTran C V
SQLFreeHandle C V
SQLGetDiagField C V
SQLGetDiagRec C V
SQLSetConnectAttr C V
SQLDescribeParam 2 V
SQLSetStmtAttr C V

Возможности параллельного входа

RDM Embedded 5.0 – первая версия RDM с возможностью параллельного входа в код на всех поддерживаемых платформах. Хотя версия RDM 4.5 поддерживала многопоточные приложения, исполняемая библиотека допускала одновременное выполнение её кода лишь одним потоком – при одновременном вызове её функций несколькими потоками все они, кроме одного, блокировались, и код RDM выполнялся последовательно. В версии RDM Embedded 5.0 исполняемая библиотека допускает одновременное выполнение кода несколькими потоками. Это может значительно повысить производительность многопоточных приложений.

Если в приложении имеется несколько потоков, вызывающих RDM или ROM, то для каждого из них необходимо открыть отдельную задачу БД (с помощью dt_opentask). Разделение задачи БД между несколькими потоками приведет к ошибке памяти и, возможно, к повреждению базы данных.

RDM Embedded 6.0 расширяет возможности параллельного входа. В его новую архитектуру эти функции заложены изначально. Для поддержки расширенных возможностей параллельного входа был несколько изменён набор API. Теперь каждая функция не имеет двух версий (d_ для одной задачи, dt_ для многозадачности) – единый набор API d_ обладает возможностями обоих прежних наборов. Для приложений, в которых изменение исходного кода для использования новой структуры API нежелательно, RDM Embedded 6.0 предоставляет большой набор соответствий, который позволяет использовать новые возможности, не изменив ни одной строки кода.

Поддержка кодировки Unicode

RDM Embedded 6.0 обеспечивает два уровня поддержки кодировки Unicode (на поддерживающих ее платформах):

  1. Поля данных Unicode (тип данных wchar_t)
  2. Функции библиотеки Unicode с аргументами и полной внутренней обработкой строк в кодировке Unicode.

Бинарные файлы этого продукта содержат по две версии каждой программы и библиотеки: версия ANSI, которая поддерживает поля данных Unicode, но принимает строковые аргументы в кодировке ANSI, а все строки обрабатываются внутри как строки ANSI (уровень 1), и версия Unicode, полностью реализованная в кодировке Unicode (уровни 1 и 2).

Кроме поддержки Unicode, функциональные возможности этих двух наборов бинарных файлов одинаковы. В приложении, которое не работает с данными на иностранном языке, использование библиотеки DLL с кодировкой Unicode не имеет смысла. Поскольку операционные системы Windows 95 и Windows 98 поддерживают Unicode не полностью, бинарные файлы Unicode рекомендуется использовать лишь тогда, когда это действительно необходимо. При разработке нового приложения RDM следует помнить, что использование Unicode ограничивает применение такого приложения операционными системами Windows NT и Windows CE.

При использовании динамической библиотеки (DLL) с кодировкой Unicode рекомендуется всегда использовать утилиты с этой же кодировкой. Аналогично, при использовании DLL в кодировке ANSI следует использовать утилиты, предназначенные для кодировки ANSI. Это продиктовано несовместимостью файлов TAF (Transaction Activity Files), создаваемых ANSI- и Unicode-версиями библиотек. Совместное использование утилит ANSI и Unicode приводит к постоянным ошибкам S_TAFSYNC (-944), при каждом возникновении которых необходимо удалять файл TAF.

Поля данных Unicode

Обе версии ddlp (ANSI и Unicode) распознают тип данных wchar_t для полей базы данных. В словаре базы данных (файл DBD) эти поля представлены буквой 'C' по аналогии с буквой 'c' для полей char. Размер этих полей зависит от системы: в Win32 размер wchar_t равен двум байтам, а в UNIX – 4 байта. К сожалению, это ведёт к несовместимости данных на различных платформах, однако в любом случае, несовместимость часто вызвана разницей в порядке байтов.

Сортировка полей Unicode

Для сортировки строк Unicode, определенных в качестве ключевых полей или используемых в отсортированных наборах, RDM Embedded использует функции исполняемой библиотеки С _wcsncoll() и _wcsnicoll(). Если опция IGNORECASE включена, используется функция _wcsnicoll(), в противном случае – функция _wcsncoll().

Чтобы определить, как обрабатывать строки, эти функции используют выбранный вариант настроек языка. Исполняемая библиотека С выбирает при запуске вариант 'C', что ведет к порядку сортировки ASCII. Однако, порядок сортировки ASCII в общем случае не подходит для иностранного языка, поэтому RDM выбирает при инициализации тот вариант языка, который принят в системе по умолчанию, восстанавливая исходные настройки при завершении (точнее, настройки языка устанавливаются при присоединении процесса к RDM DLL и сбрасываются при его отсоединении – см. libmain.c и global.c).

Языковые настройки RDM можно изменить, вызвав в приложении функцию setlocale() или _wsetlocale(). Если требуется порядок сортировки с учётом регистра (как в ASCII), следует выбрать настройки 'C'.

Обратите внимание, что при сортировке данных Unicode таблица стран Country Table в RDM не используется. Эта функция, которая поддерживалась во всех версиях RDM после 3.0, позволяет приложению переопределить порядок сортировки символьных данных. Она используется в приложениях с европейскими языками, где символы сортируются не по своим ASCII-значениям. Поскольку эти символы правильно сортируются Unicode-функциями сортировки строк, то такой механизм, как Country Table, в этом случае не требуется.

Однако таблица Country Table всё еще используется для символьных полей.

Файлы DDLP и Unicode DBD

Для Unicode-версии файла ddlp (ddlpu.exe) обычно требуется, чтобы схема DDL была текстовым файлом в кодировке ASCII, но если в командной строке указана опция '-u', то схема DDL должна содержать текст в кодировке Unicode.
Так же и заголовочный файл, созданный ddlp, обычно является текстовым файлом в кодировке ASCII, но если указана опция '-u', то текст должен быть в кодировке Unicode (Microsoft Visual C++ компилирует текстовые файлы в кодировке Unicode). Имена файлов базы данных в словаре БД (файл DBD) также будут текстом в кодировке Unicode, если указана опция '-u'. Созданный файл DBD будет иметь формат версии u5.00, отличный от формата версии 3.00, который использовался в прежних версиях RDM Embedded. DLL из RDM Embedded 5.0 может читать файлы DBD в обоих форматах, но новый формат не читается прежними версиями RDM.

Усовершенствования продукта (исправления ошибок и изменения архитектуры)

В следующих разделах перечислены некоторые изменения, внесённые в архитектуру RDM Embedded после версии 3.21a.

Новая система установки и конфигурирования

Новая версия RDM Embedded 6.0 на платформах Unix содержит дистрибутивную систему, которая использует автоматическое конфигурирование GNU.

Platform Support Packages

RDM Embedded 6.0 содержит код для всех платформ в едином наборе модулей. Вместо непосредственного вызова служб для конкретной платформы ядро СУБД использует службы PSP (Platform Support Packages – пакеты поддержки платформ). Это значительно упростило разработчикам RDM Embedded перенос и сопровождение продукта. А тем разработчикам, которые используют RDM Embedded, стало проще выполнять отладку и сопровождение.

Кроме того, службы PSP позволяют разработчикам создавать собственный переносимый код. С помощью служб PSP разработчики могут значительно сократить трудоемкость разработки и сопровождения приложений, поддерживающих несколько операционных систем.

Более длинные имена файлов

В прежних версиях RDM Embedded (до 4.5) длина имени файла, включая путь, должна была не превышать 47 знаков. Начиная с версии RDM Embedded 4.5, это ограничение увеличено до 255 знаков.

Формат базы данных (включая формат файла DBD) не изменился. Но, так как файл DBD содержит имена всех файлов данных и ключевых файлов базы данных, указанных в схеме DDL, на длину имён в схеме DDL (и в файле DBD) остаётся ограничение в 47 знаков. Поскольку в определении базы данных не рекомендуется явно указывать пути, мы надеемся, что данное ограничение не окажется слишком жестким.

Путь к файлам базы данных можно указать несколькими способами, например, в вызове d_open(), через d_dbdpath() или d_dbfpath() или в файле rdm.ini. Все эти способы позволяют указать путь длиной 255 знаков.

Следует отметить, что если программа непосредственно читает словарь базы данных RDM (файл DBD), используя определенную в файле dbtype.h структуру FILE_ENTRY, то программу следует изменить так, чтобы вместо FILE_ENTRY она использовала структуру V3_FILE_ENTRY.

Следует также помнить, что система MS-DOS и 16-разрядные программы Windows по-прежнему используют имена файлов и каталогов в формате 8.3, но они могут использовать краткие эквиваленты всех имён Windows NT и Windows 95.

Выделение кэш-памяти

RDM Embedded 4.0 и более поздние версии выделяют память для кэш-страниц только тогда, когда в них впервые возникает необходимость. Версия RDM Embedded 3.30 выделяла весь кэш при первом открытии базы данных, а версии RDM Embedded 4.0 и RDM Embedded 4.5 по умолчанию ожидают возникновения действительной потребности в кэш-памяти.

Однако можно сделать и так, чтобы вся кэш-память выделялась при первом открытии базы данных (как в RDM 3.30). Для этого нужно включить параметр запуска PREALLOC_CACHE, то есть вызвать в приложении функцию dt_on_opt(PREALLOC_CACHE, &Task) или установить в разделе [rdm] файла rdm.ini параметр prealloc_cache=1.

Проблемы диспетчера блокировки с именами файлов

В версии RDM Embedded 4.5 была решена проблема именования файлов, которая в более ранних версиях могла вызвать повреждение базы данных, особенно под системами Windows NT и Windows 95.
Диспетчер блокировки RDM Lock Manager ведёт список файлов, которые используются взаимодействующими с ним приложениями RDM. Когда приложение открывает базу данных, оно отправляет диспетчеру блокировки имена всех файлов БД. Поэтому все приложения должны использовать для идентификации этих файлов одни и те же имена. Если приложения используют для одного и того же файла различные имена, то диспетчер блокировки воспримет их в качестве разных файлов и может выдать одновременную блокировку записи нескольким приложениям для одного и того же файла. Такая ситуация может привести к повреждению базы данных.

При отправке диспетчеру блокировки списка имён файлов исполняемая библиотека БД обращается к файлам базы данных по их UNC-именам (Universal Naming Convention – соглашение об универсальном назначении имен), чтобы избежать несогласованности из-за различных имен одного и того же сетевого диска на разных рабочих станциях. UNC-имена (полные имена), состоящие из имени компьютера и пути, выглядят следующим образом:

\\machine_name\directory\filename (\\имя_компьютера\каталог\имя_файла)

Использование полных имен файлов разрешается опцией запуска LMC_OPT_TRUENAME, которая принята по умолчанию. Такой механизм использовался во всех версиях RDM Embedded, начиная с версии 3.30.

К сожалению, возможны ситуации, при которых полное имя не может быть получено 16-разрядным приложением. Два типичных примера:

Решения

1. LMC_OPT_NOPATH

Добавлена опция запуска, которая удаляет путь из всех имён файлов, отправляемых диспетчеру блокировки. Например, имя файла

C:\RDM\DB\TEST\TESTDB.D01

будет отправлено в виде:

TESTDB.D01

Это решает проблему, если управляемые диспетчером блокировки имена файлов уникальны. Однако этот способ не срабатывает, если в различных каталогах есть несколько экземпляров одной базы данных и они открыты в режиме совместного ("s") или монопольного ("x") доступа. Кроме того, эта опция не поддерживается всеми 32-разрядными версиями RDM Embedded.

Чтобы включить эту опцию, либо добавьте в файл rdm.ini следующие строки:

[rdm]
truename=0
nopath=1

либо перед вызовом dt_open() вызовите следующие функции:

dt_off_opt(LMC_OPT_TRUENAME, &Task);
dt_on_opt(LMC_OPT_NOPATH, &Task);

2. Функция обратного вызова полного имени

В приложении можно использовать функцию обратного вызова, которая выполняет преобразование кратких имен файлов в полные имена. RDM вызовет эту функцию, когда возникнет необходимость в преобразовании имени файла в формат UNC и отправки его диспетчеру блокировки (при открытой базе данных). Функция обратного вызова вызывается однократно для каждого преобразуемого имени файла, которое передается в качестве аргумента. Она может преобразовать имя файла в формат UNC или не выполнить никакого действия, результат ее выполнения указывается возвращаемым значением.

Для использования функции обратного вызова необходимо выполнить следующее:

Средства отладки

Начиная с версии 4.0, в RDM Embedded имеется несколько отладочных переключателей, помогающих разработчикам при поиске и устранении проблем, связанных с исполняемой библиотекой. Доступ к ним открывается после перекомпиляции библиотеки или DLL с определением опции DB_DEBUG.
Если опция DB_DEBUG определена, то с помощью вызова функции d_on_opt() или при добавлении записей в раздел [debug] файла rdm.ini можно использовать следующие параметры запуска:

Опция Описание
PZVERIFY При обращении к файлу базы данных, значения его переменных next_slot и delete_chain сравниваются с фактическим размером файла, позволяя убедиться, что они не указывают за пределы файла. Это помогает обнаружить проблему некорректного увеличения размера файла.
PAGE_CHECK Каждая считанная в кэш-буфер страница базы данных будет помечена идентификационным номером, позволяющим отслеживать ее в памяти и проверять после записи. Это обеспечивает лишь минимальный уровень проверки, поэтому польза от этой опции для разработчика незначительна.
LOCK_CHECK После каждого защищенного блокировкой обращения к файлу базы данных или к странице кэш-буфера извлекается дата последнего изменения файла, а с диска считываются первые 4 байта страницы, содержащие время ее последнего изменения. Это позволяет убедиться в том, что после блокировки файл не был некорректно обновлен другим процессом. Данная опция может выявить некорректное разрешение конфликта блокировки из-за неверного имени файла TAF, ошибок диспетчера блокировки или неверных имён файлов БД. Это позволяет обнаружить общую проблему диспетчера блокировки с дублированием имен файлов, вызванную тем, что не все процессы одинаково используют опцию TRUENAME. !!! ВНИМАНИЕ!!! Эта опция выполняет дисковые операции ввода/вывода при каждом обращении к внутреннему кэш-буферу, что существенно снижает быстродействие программы.
CACHE_CHECK Структуры данных внутреннего кэш-буфера проверяются на целостность в начале каждой функции d_function и после каждого обращения к кэш-буферу.
Кроме того, DB_DEBUG также определяет опцию DB_TRACE, которая позволяет исполняемой библиотеке записывать различные результаты трассировки в файл на диске. Имя этого файла постоянно – "vista.out", а его местонахождение можно изменить установкой новой переменной среды DBTPATH. Файл трассировки может совместно использоваться несколькими процессами в сети, при этом конфликты разрешаются с помощью блокировок. При каждой записи DB_TASK в этом файле записывается также строка, идентифицирующая процесс и задачу.
TRACE_DBERR При использовании этой или любой другой опции трассировки все ошибки базы данных записываются в файл трассировки сразу перед вызовом обработчика dberr.
TRACE_API Записывается каждая функция d_function. Поскольку до первого вызова d_open файл rdm.ini не обрабатывается, трассировка любой ранее открытой функции d_ выполняться не будет, если не определена переменная среды DBTRACE. Если она определена, то все опции трассировки будут действовать до тех пор, пока не будут отменены файлом rdm.ini.
TRACE_LOCKS Выполняется запись при каждой блокировке файла и при каждом снятии блокировки.

 

Примечание. Если опция SYNCFILES включена (настройка по умолчанию), то при каждой записи файл трассировки физически записывается на диск. Это предотвращает потерю информации в случае завершения работы программы с ошибкой GP или при зависании компьютера, но очень сильно нагружает диск.

В файл rdm.ini добавлен новый раздел для указания параметров отладки и трассировки:

[debug]  
pzverify=1 сравнение pgzero с длиной файла при каждом обращении
page_check=1 проверка кэш-страниц
lock_check=1 обнаружение некорректных обновлений файла
cache_check=1 проверка целостности данных кэш-буфера
trace_dberr=1 запись сообщений dberr в файл трассировки
trace_api=1 запись всех функций d_api в файл трассировки
trace_locks=1 запись в файл трассировки информации об установке и снятии блокировок

Эти параметры можно также установить с помощью d_on_opt() и d_off_opt(), но в одном вызове d_on_opt() или d_off_opt() нельзя смешивать параметры отладки, трассировки и другие параметры.

d_on_opt(PZVERIFY);
d_on_opt(TRACE_DBERR | TRACE_API); /* должен быть отдельный вызов */

Для управления выходной информацией трассировки добавлены две переменные среды:

set DBTPATH=<path> ; указывает расположение файла трассировки "vista.out"
set DBTRACE=1 ; включает полную трассировку при запуске программы

Обратите внимание – если библиотека или DLL компилировалась без определений DB_DEBUG и DB_TRACE, то эти функции библиотеки не будут действовать, а все ссылки на эти параметры будут игнорироваться.

Вопросы совместимости

Совместимость диспетчеров блокировки

В одной многопользовательской системе нельзя одновременно запустить RDM Embedded 6.0 и более старую версию RDM, которые совместно используют базы данных. У версии RDM 6.0 и прежних версий несовместимы следующие структуры данных:

При переходе со старой версии RDM на новую, удалите все файлы TAF, LOG и DBL.
Обратите внимание, что версии Unicode и ANSI вышеупомянутых структур также несовместимы. Вследствие этого, в одной многопользовательской системе нельзя одновременно запускать приложения RDM версий Unicode и ANSI.

Совместимость баз данных

Базы данных, созданные с помощью более ранних версий RDM Embedded, совместимы с версией RDM Embedded 5.0. Формат данных и ключевые файлы в версии RDM Embedded 5.0 не изменились. Хотя в версии 5.0 был введен новый формат словаря базы данных (DBD), RDM Embedded 5.0 понимает старый формат и может генерировать файлы DBD в старом формате.

Введение нового формата словаря БД (DBD) было обусловлено поддержкой длинных имен файлов и кодировки Unicode, поскольку имена всех файлов данных и ключевых файлов БД содержатся в файле DBD.

RDM Embedded 6.0 поддерживает следующие форматы DBD:

v3.00 Используется всеми версиями RDM Embedded с 3.0 по 4.5 с данными ANSI и именами ключевых файлов длиной до 48 знаков.
v3.01 Ранее этот формат распознавался только в RDM/Vx 4.9.3 с расширениями DDL для VxWorks
v5.00 Имена файлов в кодировке ANSI длиной до 256 знаков и расширения v3.01.
u5.00 Имена файлов в кодировке Unicode длиной до 256 знаков и расширения v3.01.

Эти версии DBD поддерживаются версиями RDM, указанными ниже в таблице:

 

  RDM Embedded 3.0 – 4.5 RDM Embedded 4.9.3 RDM Embedded 5/6 Ansi RDM Embedded 5/6 Unicode
V3.00 Да Да Да Да
V3.01 Нет Да Да Да
V5.00 Нет Нет Да Да
U5.00 Нет Нет Нет Да

Совместимость приложений

Интерфейс API в RDM Embedded 6.0 немного отличается от двух API в RDM Embedded 5.0 и более ранних версиях RDM, поэтому новая версия может выполнять преобразование из прежних API в новый вид RDM Embedded 6.0. Программисты могут оставить существующие приложения без изменений, либо изменить исходный код с учетом нового API, используя одно из преобразований. Чтобы использовать преобразование, следует определить константы препроцессора либо в командной строке, либо в исходном коде приложения до заголовков RDM Embedded 6.0.

Приложения, использующие преобразования, должны содержать заголовок vista.h, как и в предыдущих версиях. Чтобы преобразовать dt_ api версии RDM Embedded 5.0 в новую форму RDM Embedded 6.0, определите препроцессорный символ RDM_LEGACY_API. Чтобы преобразовать d_api версии RDM Embedded 5.0 без параметра задачи БД, кроме RDM_LEGACY_API определите также и RDM_TASK.

Изменения в диспетчере блокировки

Диспетчер блокировки входит в число наиболее сложных компонентов системы RDM Embedded, поэтому с ним связано большинство вопросов по RDM, поступающих в компанию Birdstep Technology. После версии RDM Embedded 3.21a исходный код диспетчера блокировки претерпел сотни обновлений и изменений. Фактически, пришлось полностью отказаться от управления множественными блокировками. В версии RDM Embedded 6.0 поддерживаются следующие диспетчеры блокировки:

Системы Win32

Системы типа Unix

Каждый из этих диспетчеров блокировки изначально более стабилен в сравнении c любыми диспетчерами, существовавшими на момент выпуска RDM Embedded 3.21a. В следующем разделе описаны исправления для платформы QNX и вопросы, касающиеся других платформ.

Описание предыдущей версии продукта

Описание Birdstep RDM Server

Описание Birdstep RDM Mobile

Технические характеристики СУБД Birdstep

За дополнительной информацией обращайтесь в компанию Interface Ltd.

Рекомендовать страницу

INTERFACE Ltd.
Телефон/Факс: +7 (495) 925-0049
Отправить E-Mail
http://www.interface.ru
Rambler's Top100
Ваши замечания и предложения отправляйте редактору
По техническим вопросам обращайтесь к вебмастеру
Дата публикации: 03.04.03