Обновите буфер и верните режим блокировки к исходному

Допустим, у нас есть процедура, которая принимает buffer-параметр:

myprocedure.p:

DEF PARAM BUFFER bufferParameter FOR DatabaseTable.

/* Get the same buffer in EXCLUSIVE-LOCK for updating */
FIND CURRENT bufferParameter EXCLUSIVE-LOCK.

/* Update the value */
ASSIGN bufferParameter.MyField = "new value".

/* Reset the locking mode back to the original one? */
FIND CURRENT bufferParameter NO-LOCK.

Проблема с этим подходом в том, что он, возможно, изменяет исходный locking-mode переданного buffer parameter. Возможно, что буфер был в EXCLUSIVE-LOCK или f.ex в NO-LOCK. Изменив locking-mode, эта процедура могла вызвать ошибки обновления в некоторых других программах, использующих тот же buffer.

Другое решение - создать новый temporary buffer для той же таблицы, а затем обновить базу данных через этот буфер (не касаясь переданного буфера параметров). Минус этого подхода в том, что переданный оригинал buffer parameter станет «устаревшим». Потребуется новый запрос к базе данных, чтобы обновить его значение 'MyField', чтобы оно соответствовало значению в базе данных.

Как мне восстановить locking-mode исходный буфер буфера после его fields обновления?


person W0lfw00ds    schedule 30.03.2017    source источник
comment
При всем уважении, я думаю, вы задаете неправильный вопрос. Я подозреваю, что вам действительно нужно знать, как правильно обновить поле, не вызывая непреднамеренных конфликтов блокировок.   -  person Tom Bascom    schedule 30.03.2017


Ответы (5)


Эта ветка может быть полезной в качестве фона - она ​​рассказывает о том, как определить текущий статус блокировки.

http://stackoverflow.com/questions/26136842/progress-4gl-display-buffer-lock-type?rq=1

В конечном итоге я думаю, что это то, что вам действительно нужно:

    /* lightly tested psuedo code...
     */

    procedure updateMyField:

      define input parameter myRowid  as rowid.

      define buffer updMyTable for myTable.  /* this limits the scope of the buffer to this procedure */

      do for updMyTable transaction:  /* FOR "strong scopes" the buffer to this block */
        find updMyTable exclusive-lock where rowid( updMyTable ) = myRowid no-error.  /* the EXCLUSIVE-LOCK is scoped the FOR block -- when we leave the block it is released */
        if available updMyTable then
          assign
            updMyTable.myField = "new value"
          .
         else
          do:
            message "could not get an exclusive-lock on myTable".
            /* or whatever error handling you need... */
          end.
      end.  /* the transaction is committed and the lock is released -- no need to change its status */

      return.

    end.

    /* main block
     */

    find first myTable no-lock.
    display myTable.myField.
    pause.

    run updateMyField ( rowid( myTable )).

    find first myTable no-lock.
    display myTable.myField.
    pause.
person Tom Bascom    schedule 30.03.2017

Странно, что тип блокировки не является атрибутом дескриптора буфера. Казалось бы, очевидная необходимость.

Вы можете найти записи _lock для recid, но вам также нужно найти идентификатор таблицы, что означает поиск его в _file из дескриптора: table.

Итак, если у вас подключена только одна база данных, у вас будет что-то вроде:

FIND _file NO-LOCK WHERE _file-name = bufHandle:TABLE.
FIND _lock NO-LOCK WHERE _lock-name = USERID()
    AND _lock-table = _file-number
    AND _lock-recid = bufHandle:RECID.

Если запись существует, вы знаете, что у вас есть блокировка. _Lock_flags сообщит вам, какие у вас блокировки. Я думаю, что S - это общий доступ, а E - эксклюзивный, и думаю, что если есть Q, то вы ждете этой блокировки, чего я бы не ожидал, если у вас уже есть блокировка для записи.

Затем вы можете повторно заблокировать запись в соответствующем состоянии, если она еще не была заблокирована исключительно при запуске вашей функции.

person Screwtape    schedule 30.03.2017
comment
До OE 11.4 в любой нетривиальной системе запрос _LOCK, подобный этому, будет работать очень и очень плохо. Пост 11.4 может быть только плохо. - person Tom Bascom; 30.03.2017
comment
Хороший момент - я действительно удивлен, что нет атрибута буфера для статуса блокировки. Кажется, что какой-то функции не хватает, но я все еще использую 9.1, поэтому многие функции отсутствуют! - person Screwtape; 30.03.2017
comment
Есть атрибут LOCKED. Это эквивалентно функции LOCKED (). - person Tom Bascom; 30.03.2017
comment
Я видел это, но объяснение заблокированного атрибута в руководстве, похоже, подразумевало, что он ожидал блокировки, а не удерживал ее, и в нем не говорится, какой именно тип, поскольку он является логическим. - person Screwtape; 30.03.2017
comment
Это правильно. По сути, это говорит вам, что вы не можете заблокировать запись, потому что это уже сделал кто-то другой. Я просто указываю на то, что доступна информация о / some / lock. Я, вероятно, полон болтовни или расщепления волос (или того и другого), но я думаю, что Progress может сказать, что проблема в том, что блокируется не БУФЕР, а ЗАПИСЬ. Мне это кажется неудовлетворительным, но я подозреваю, что все это происходит из-за кроличьей норы. - person Tom Bascom; 30.03.2017

Вы не можете разблокировать такую ​​запись, когда находитесь в транзакции. Если вы попытаетесь, монопольная блокировка превратится в блокировку неопределенности до тех пор, пока транзакция не завершится и изменения не будут зафиксированы в базе данных. Это сделано для предотвращения проблем с целостностью данных. Если вы смотрите на поле _Lock-Flags, блокировка лимба будет буквой «L». Вы также можете использовать Promon для проверки статуса таблицы блокировок.

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

MESSAGE TRANSACTION VIEW-AS ALERT-BOX.

Вот документация OpenEdge по блокировке записей и области транзакций: https://documentation.progress.com/output/ua/OpenEdge_latest/index.html#page/gsabl/handling-data-and-locking-records.html#

person TheDrooper    schedule 30.03.2017
comment
Итак, в двух словах ... Если я использую FIND CURRENT mybuffer EXCLUSIVE-LOCK. в этой процедуре, транзакция, возможно, завершится при выходе из этого файла. При выходе из области транзакции locking-mode сбрасывается на NO-LOCK или возвращается в исходный режим блокировки, который использовался ранее? - person W0lfw00ds; 31.03.2017
comment
Если вы уже нашли запись с EXCLUSIVE-LOCK перед запуском этой процедуры, транзакция будет ограничена до вызывающей процедуры. Другие процессы не смогут заблокировать запись, пока эта транзакция не завершится. - person TheDrooper; 01.04.2017

Я думаю, что лучше всего использовать ваше второе решение. Определите локальный буфер (нет необходимости называть его временным буфером, который не является четко определенным термином в OpenEdge IMO), загрузите запись параметра в этот буфер, используя rowid (в основном бесплатная операция, если буфер параметров уже был заблокирован) и измените свой локальный буфер. Не беспокойтесь о том, что что-то устареет. Цитата http://knowledgebase.progress.com/articles/Article/P4548 (пожалуйста прочтите статью полностью)

В пуле записей клиента одновременно может существовать только одна копия записи. Это означает, что в следующем случае показаны два буфера, указывающие на одну и ту же физическую копию клиента 1. Это означает, что после выполнения следующего кода

DEFINE BUFFER cust FOR customer.
DEFINE BUFFER cust1 FOR customer.
FIND FIRST cust no-lock.
FIND cust1 where rowid(cust1) = rowid(cust) exclusive-lock.
cust1.name = 'xyz':U.

cust.name также будет иметь значение xyz. Я просто попробовал код, чтобы убедиться. На самом деле я также подтвердил, что блокировка не в буфере, а в записи. Я также мог бы назначить cust.name, даже если в find для cust не используется блокировка. Если за пределами вашей программы / процедуры транзакции еще нет, то при выходе из процедуры блокировка будет снята. Определенно не пытайтесь работать с _lock, если это не необходимо, чтобы показать пользователю, который блокирует запись, которую он хочет изменить (или в аналогичных целях).

person idspispopd    schedule 31.03.2017

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

Итак, хороший способ обновить переданный буфер - получить ту же запись от ROWID и отдельно обновить ее в другом буфере:

DEF BUFFER myFirstBuffer FOR MyTable.
DEF BUFFER mySecondBuffer FOR MyTable.

FIND FIRST myFirstBuffer WHERE myFirstBuffer.MyField = "myvalue" NO-LOCK.

/* At this moment myFirstBuffer.MyField is "myvalue" */

FIND FIRST mySecondBuffer WHERE ROWID(mySecondBuffer) = ROWID(myFirstBuffer) EXCLUSIVE-LOCK.
ASSIGN mySecondBuffer.MyField = "mynewvalue".

/* Now both 'myFirstBuffer.MyField' and 'mySecondBuffer.MyField' match ("mynewvalue")! */

Progress, кажется, обрабатывает все изменения данных и соответственно обновляет буферы. Однако я считаю, что это не сработает, если запущены 2 отдельные программы, f.ex myprogram1.p и myprogram2.p, и они обе обновляют одну и ту же запись в разное время (хотя это уже другая история ...). Раньше у нас были проблемы с TRIGGERs, когда затронутые триггером поля не обновлялись в буфере.

Но вкратце, я считаю, что лучше всего запросить запись отдельно с помощью ROWID и обновить ее таким образом. Я действительно считаю, что разработчик не должен использовать FIND CURRENT myBuffer NO-LOCK., поскольку это изменит locking-mode записи и может нарушить логику использования myBuffer позже.

person W0lfw00ds    schedule 31.03.2017
comment
Вам следует использовать ROWID, а не RECID. RECID устарел. ROWID более портативен и ориентирован на будущее. - person Tom Bascom; 31.03.2017
comment
Разбить его на две отдельные программы в рамках одного сеанса можно. Но если вы не будете осторожны с масштабом, у вас могут возникнуть проблемы, которые будет трудно решить. В коде 4gl очень распространена практика разбрасывать свободные ссылки на буферы повсюду. Это в основном безвредно, когда все ссылки БЕЗ БЛОКИРОВКИ. Это становится проблемой, когда обновления происходят в этом буфере, и объем транзакции внезапно становится всей содержащей процедурой. Использование именованного буфера обновления со строгой областью видимости для блока FOR позволяет избежать таких проблем. - person Tom Bascom; 31.03.2017