USACE / USACE/cwms-data-api

Project Lock Request needs to be able to work equivalently to how it currently works from REGI

Open
#711 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Java
Stars
28
Forks
25
Avg merge
4d 22h
Merged PRs (30d)
20

Description

Currently in REGI a user can get some project locks. When a lock is acquired the database grabs the username, os_user, machine_name and program out of the database sesssion and stores those into the lock. The user gets back a lock id (guid).

REGI does not persist the lock_id.

If REGI gets some locks and then is killed it has no chance to return/release the locks.

When REGI starts after a crash it can query the db for project locks and realize that there are locks in the db for the current user that the current instance doesn't know about. When that is detected REGI can prompt the user to sort it out.

If we just call the same pl/sql procs from CDA then CDA is going to pull the username, os_user, machine_name, program of the CDA server.

Unless we do something os_user, machine_name and program will be the same for all CDA calls.
CDA might be sticking the db_user into username - to be verified. But even if that is true its likely the database username not the REGI username.

So far one proposed solution is to refactor REQUEST_LOCK into two procedures. One that pulls the values out of the env and one that does the actual work like:

function request_lock2(
    p_project_id      in varchar2,
    p_application_id  in varchar2,
    p_revoke_existing in varchar2 default 'F',
    p_revoke_timeout  in integer  default 30,
    p_office_id       in varchar2 default null,
    p_username        in varchar2,
    p_osuser          in varchar2,
    p_program         in varchar2,
    p_machine         in varchar2)
    return varchar2
is
    revocation_denied exception;
    already_locked    exception;
    pragma            exception_init(revocation_denied, -20998);
    pragma            exception_init(already_locked,    -00001);
    pragma            autonomous_transaction;

    l_lock_id         varchar2(40);
    l_do_lock         boolean := true;
    l_office_id       varchar2(16);
    l_already_locked  boolean := false;
    l_id              integer;
    l_queue_name      varchar2(61);
    l_text_msg        varchar2(32767);
begin
    -------------------
    -- sanity checks --
    -------------------
    if p_revoke_existing is null then
        cwms_err.raise('NULL_ARGUMENT', 'p_revoke_existing');
    end if;
    if p_revoke_timeout is null then
        cwms_err.raise('NULL_ARGUMENT', 'p_revoke_timeout');
    end if;
    -----------------
    -- do the work --
    -----------------
    if is_locked(p_project_id, p_application_id, p_office_id) = 'T' then
        if p_revoke_existing in ('T', 't') then
            begin
                revoke_lock(
                        p_project_id,
                        p_application_id,
                        p_revoke_timeout,
                        p_office_id);
            exception
                when revocation_denied then l_do_lock := false;
            end;
        else
            l_do_lock := false;
        end if;
    end if;
    if l_do_lock then
        l_lock_id := rawtohex(sys_guid());

        begin
            insert
            into at_project_lock
            ( lock_id,
              project_code,
              application_id,
              acquire_time,
              session_user,
              os_user,
              session_program,
              session_machine
            )
            values ( l_lock_id,
                     cwms_loc.get_location_code(p_office_id, p_project_id),
                     lower(p_application_id),
                     systimestamp at time zone 'UTC',
                     p_username,
                     p_osuser,
                     p_program,
                     p_machine
                   );
        exception
            when already_locked then
                ----------------------------------------------
                -- encountered a race condition and another --
                -- lock attempt beat us to the punch        --
                ----------------------------------------------
                l_already_locked := true;
        end;
        if l_already_locked then
            l_lock_id := null;
        else
            commit;
            ------------------------------
            -- publish the state change --
            ------------------------------
            l_office_id := cwms_util.get_db_office_id(p_office_id);
            l_queue_name  := l_office_id||'_'||'STATUS';
            l_text_msg := '
            <cwms_message type="State">
               <property name="new state"   type="String"> locked        </property>
               <property name="old state"   type="String"> unlocked      </property>
               <property name="action"      type="String"> lock acquired </property>
               <property name="office"      type="String"> $office       </property>
               <property name="project"     type="String"> $project      </property>
               <property name="application" type="String"> $application  </property>
               <property name="user"        type="String"> $user         </property>
            </cwms_message>';
            l_text_msg := replace(l_text_msg, '$office',      l_office_id);
            l_text_msg := replace(l_text_msg, '$project',     dbms_xmlgen.convert(p_project_id));
            l_text_msg := replace(l_text_msg, '$application', dbms_xmlgen.convert(lower(p_application_id)));
            l_text_msg := replace(l_text_msg, '$user',        cwms_util.get_user_id);
            l_id := cwms_msg.publish_message(l_text_msg, l_queue_name, true);
        end if;
    end if;
    return l_lock_id;
end request_lock2;


function request_lock(
   p_project_id      in varchar2,
   p_application_id  in varchar2,
   p_revoke_existing in varchar2 default 'F',
   p_revoke_timeout  in integer  default 30,
   p_office_id       in varchar2 default null)
   return varchar2
is
   l_username        varchar2(30);
   l_osuser          varchar2(30);
   l_program         varchar2(64);
   l_machine         varchar2(64);

begin
    select username,
           osuser,
           program,
           machine
    into l_username,
        l_osuser,
        l_program,
        l_machine
    from v$session
    where sid = sys_context('userenv', 'sid');

    return request_lock2(
        p_project_id,
        p_application_id,
        p_revoke_existing,
        p_revoke_timeout,
        p_office_id,
        l_username,
        l_osuser,
        l_program,
        l_machine);

end request_lock;

There may be downsides - not sure how to verify that users are who they claim to be.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the existing request_lock procedure, the proposed request_lock2 interface, the at_project_lock columns, and the v$session lookup described in the issue. Determine and document how CDA should supply and verify lock identity, then confirm equivalent lock acquisition, conflict handling, release behavior, and user-facing recovery after a REGI crash.

Written by the indexing model from the issue text.

Assessment

Tech stack
sql
Domain
api, database
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.