При проведении документа работа с документами другого пользователя зависает до окончания проведения.
Это легко можно воспроизвести, запускаем две версии 1с под разнами 1с пользователями, одну бд. В одной проводим документ, в другой, есть открыть документы, все подвисает, ждет коммита. Что бы затянуть мироприятие, можно в конфигураторе добавить в функцию провидения: Предупреждение("!!!",0); Надо разобраться, можно ли как-нибудь избавится от этого. В 1с8 вроде можно увидеть тот же эффект при работе с Postgres. Что конечно не внушает особого оптимизма.
Итак, ситуация такая: При проведении 1с принудительно блокикует таблицу _1sjourn: {call _1sp__1SJOURN_TLockX} Create procedure _1sp__1SJOURN_TLockX AS set nocount on declare @i integer select @i=1 from _1SJOURN(TABLOCKX HOLDLOCK) where 0=1 GO Блокировка TABLOCKX HOLDLOCK соответствует postgresовской ACCESS EXCLUSIVE, и закрывает доступ даже к SELECT'у. Т.е. [SELECT * FROM _1SJOURN;] подвиснет на время блокировки. Но в mssql есть NOLOCK и [SELECT * FROM _1SJOURN(NOLOCK);] выполнится. А что-то у postgres'а я такой чтуки не видел.
Судя по всему такого в postgres нет. Эквивалент блокировки, который использует SELTA "ACCESS EXCLUSIVE" -- это полный аналог TABLOCKX HOLDLOCK у ms sql. Про эту блокировку(ACCESS EXCLUSIVE) пишут: This mode guarantees that the holder is the only transaction accessing the table in any way. Т.е. аналога NOLOCK для таких блокировок у postgres нет. Для postgres есть ISOLATION LEVEL примерно для тех же целей. Но ведет он себя по другому. Хочется посмотреть правда ли в 1с8 все так же, чего-то не смог завети 1с8 на postgre.
Склоняюсь к тому что бы просто отменить проверку на изменение. Какая разница первое сохранится или последнее изменение. Остается непонятным, как при такой крутой работе блокировок у mssql так оказалось, что данные начали менятся, когде где-то уже начались изменения. Написал в odbc драйвер, что бы тот при появлении этой ошибки писал в консоль какой запрос это вызвал. Goga это отправил пользователям.
Последнее сообщедие (#4) для предгозначалось для баги 2544.
Для адекватного воспроизведения блокировок ms sql похоже надо целиком изменить блокировки в SELTA, а именно: 1. ms: SELECT * FROM foo(TABLOCKX HOLDLOCK) Используется для хранимых процедур foo_TLockX Сейчас заменяется на pg:LOCK TABLE foo IN ACCESS EXCLUSIVE MODE; А надо на pg:LOCK TABLE foo IN EXCLUSIVE MODE; 2*. Тогда можно будет использовать "грязное" чтение ms:SELECT * FROM foo(NOLOCK); pg:SELECT * FROM foo; 3*. И "чистое" чтение. ms:SELECT * FROM foo; pg:SELECT * FROM foo FOR UPDATE; *сейчас (NOLOCK) игнорируется. 4. ms: SELECT * FROM foo(TABLOCK HOLDLOCK) Сейчас используется pg:LOCK TABLE foo IN EXCLUSIVE MODE; А надо на pg:LOCK TABLE foo IN SHARE MODE; Это работает с 2 и 3. Я посмотрел на первый взгляд работать начинает аналогично с ms sql, но надо сделать test. Например в транзакции SELECT * FROM foo FOR UPDATE; блокирует таблицу а вне транзакции не блокирует. Надо узнать все ли также.
Пока тест не дописал. Но узнал Что SELECT * FROM foo FOR UPDATE; (Чистое чтение ) блокирует таблицу IN EXCLUSIVE MODE а надо IN SHARE MODE Нашел, есть: SELECT * FROM foo FOR SHARE; Это подходит лучше.
Тест дописал. Но еще результаты не разобрал. Пока вроде на удивление все одинаково.
Нашел разницу в поведении. ms: SELECT * FROM foo Все таки не блокирует данные. А собирались его менять на SELECT * FROM foo FOR SHARE; который блокирует данные. Для 1с это мне кажется не очень важно, т.к. ну чуть заблокировали, все лучше, чем сразу блокировать журнал на чтение. Можно было бы сделать так: SAVEPOINT spselta; SELECT * FROM foo FOR SHARE; ROLLBACK TO spselta; Ничего бы не блокировалось, но SAVEPOINT работает только в транзакции, а при использовании снаружи транзакции выдает ошибку. Можно отследить открыта ли транзакция, но пока я знаю как. SQL функций похоже нет. Есть libpq PQtransactionStatus. Не знаю куда бы ее прикрутить.
Что-то с PQtransactionStatus похоже не получится. В odbc по умолчанию libpq не подключается (библиотека подключается, но подключение по нему(PQconnectdb) не происходит) есть #define DEFAULT_SSLMODE, если его изменить, то подключение будет проходить, но команды SQL все равно проходят не через libpq. Пожалуй придется делать в odbc драйвере statement, там выполнять SAVEPOINT spselta; и ROLLBACK TO spselta; и не обращать внимания на ошибки. Но они будут валится в лог postgres, что неприятно. Может можно этого избежать.? Надо будет попробывать.
(In reply to comment #10) > Что-то с PQtransactionStatus похоже не получится. В > odbc по умолчанию libpq не подключается > (библиотека подключается, но подключение > по нему(PQconnectdb) не происходит) есть #define Каким же образом происходит подключение и работа ODBC-драйвера с сервером? > Пожалуй придется делать в odbc драйвере > statement, там выполнять SAVEPOINT spselta; и ROLLBACK TO > spselta; и не обращать внимания на ошибки. Но > они будут валится в лог postgres, что неприятно. > Может можно этого избежать.? Надо будет > попробывать. Так а в транзакцию же вводится командой SQL, нельзя ставить флаг, что мы вошли в транзакцию?
(In reply to comment #11) > Каким же образом происходит подключение и > работа ODBC-драйвера с сервером? Подключение через функцию работы с сокетами connect, данные посылаются send, принимаются recv. Это в обычном режиме, есть еще SSL. > Так а в транзакцию же вводится командой SQL, Начинает транзакцию командой SQL, но заканчиваться могут odbc командой SQLEndTran, 1с так и делает. > нельзя ставить флаг, что мы вошли в > транзакцию? Думаю можно, надо поробывать.
С 1с вообще просто все. Она пользется только неявными транзакциями, те устанвливают SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, -6); И дальше что идет, все транзакция, если закончили транзакцию -- EndTran, сразу началась следующая, пока не отменят это: SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OM, -6); Т.е. надо только отслеживать статус Connection'а. Другле дело явные транзакции BEGIN TRANSACTION; COMMIT TRANSACTION; Но они в SELTA даже не транслируются. Ух ты нашел свойсво у Connection'а transact_status, отслеживает даже явные транзакции, самому ничего делать не надо! Таким образом можно передавать в selta.dll состояние транзакции, а парсер в зависимости от этого будет выбирать какую команду использовать. SAVEPOINT spselta; SELECT * FROM foo FOR SHARE; RELEASE SAVEPOINT spselta; или просто SELECT * FROM foo FOR SHARE;
Заметил, 1. у нас еще в трансляторе есть блокировка UPDLOCK. У нас она заменяется на FOR SHARE, что не правильно, т.к. это эксклюзивная блокировка. 2. Думал делать так, например ms: SELECT * FROM foo(TABLOCKX HOLDLOCK) заменять на SELECT * FROM foo FOR UPDATE, но у ms это табличная блокировка, а то как я написал для postgres -- это посточная. А табличная только LOCK TABLE table1 IN EXCLUSIVE MODE; но она разрешает чистое чтение SELECT * FROM table1 FOR SHARE, что не соответствует ms. Похоже придется как-то комбинировать.
Уже почти все хорошо, оталось только два вопроса: 1 Единственное несоответствие, которое я нашел это в UPDLOCK. У нас будет табличная блокировка, а у MS она на уровне строк. Эта блокировка разрешает чистое чтение. В postgre такое не получится. Чистое чтение у нас реализуется через FOR SHARE, что не не сможет выполнится из-за блокировки. 2. Создаст ли postgres курсор на такой запрос и нормально ли будет в нем работать: SAVEPOINT spselta; SELECT * FROM table1 FOR SHARE; ROLLBACK TO spselta; Особенно беспокоет, откатит ли изменения назад ROLLBACK TO spselta, т.к. RELEASE SAVEPOINT TO spselta не отменяет блокировку. Если это так, то придется тоже блокировть данные даже при простом чистом чтении.
(In reply to comment #15) > 2. На такой запрос курсор конечно создасться не сможет: > SAVEPOINT spselta; SELECT * FROM table1 FOR SHARE; ROLLBACK TO spselta; Но можно из selta.dll передовать надобность о курсоре в odbc драйвер, а там перед командой открывать SAVEPOINT, а после закрывать. ROLLBACK TO spselta; не отменяет изменения в курсоре, курсор не закрывает.
Created attachment 1169 [details] Миграция блокировок ms на postgres
Created attachment 1170 [details] Вывод теста блокировок для ms.
Created attachment 1171 [details] Вывод теста блокировок для pg.
Закомитил тест. Лежит в /tests/testlock/. Тест блокирует какую нибудь таблицу и смотрит, что с ней можно сделать. В тесте в testlock.cpp есть строки #define MS_SQL_SERVER /*#define PG_SQL_SERVER*/ одна и них должна быть закоментирована. В pg работает через SELTA. Вывод теста есть здесь(Вывод теста блокировок для ms. и Вывод теста блокировок для pg. удобно смотреть meld'ом) В /var/ftp/tmp/stas/testlock/ есть уже собранные.
С новыми блокировками не виснет. Закрываю.