SAP / node-rfc

Asynchronous, non-blocking SAP NW RFC SDK bindings for Node.js
Apache License 2.0
251 stars 73 forks source link

RFC Call option "KEEPING LOGICAL UNIT OF WORK" #172

Closed DenisTis closed 3 years ago

DenisTis commented 4 years ago

Dear Srdjan,

thank you a lot for the functionality of this package!

In my case I need to extract big abap table to external source. For that, in first call to ABAP I have to open cursor, and in all consecutive calls do "fetch next cursor..package size" to get data in small chunks per call. The only option to make this scenario work with RFC is to have addition during RFC Call: "KEEPING LOGICAL UNIT OF WORK". (Otherwise commit is done by the end of every RFC call).

Is it possible to achieve with the package?

Ulrich-Schmidt commented 3 years ago

Hi Denis,

at the moment, this feature is not available in external RFC programs. The problem is basically: The ABAP command "KEEPING LOGICAL UNIT OF WORK" suppresses the roll-out/roll-in of the current workprocess/user session. However, in an external program, there is no roll-out/roll-in mechanism anyway! So there is nothing that could be suppressed... In this case we would have to prevent the roll-out/roll-in on the other side, as that is the one that's destroying the open database cursor. But the behaviour of the kernel/workprocess on the other side cannot be influenced "from remote", only "from within".

It may be possible to achieve such a "small chunk" scenario, if you store the current line number (up to which you have already selected the lines of the table) in a variable in the ABAP session memory, and then select starting at that line number, when the next RFC request from the external program comes in?! The ABAP session memory stays alive for as long as the RFC connection remains open.

I also have a different idea that could work, but I have to check a few details first.

Ulrich-Schmidt commented 3 years ago

OK, the following should work. As mentioned last time, the client side (your Python program) has no control over the roll-behavior of the server-side (the SAP system). "KEEPING LOGICAL UNIT OF WORK" only suppresses the roll-out/roll-in of the current process in which it is executed, so even if something like this would be available in the RFC library, it would only keep your Python program from rolling (which it doesn't do anyway...) :-)

The solution is to switch sides: instead of an RFC client program you write an RFC server program, which listens for incoming requests. Then on ABAP side you write a report that does the following things:

  1. Get a lock on the DB table (to prevent others from inserting/deleting lines while you have an open Cursor for it)
  2. Open the DB Cursor
  3. Loop over the Cursor and in each iteration make a "CALL FUNCTION ... DESTINATION your server program KEEPING LOGICAL UNIT OF WORK" sending the currently selected lines in a TABLE parameter
  4. Close the Cursor
  5. Release the lock

Note however, that KEEPING LOGICAL UNIT OF WORK is an internal statement and SAP recommends to not use it, because bad things can happen, if you do. Basically it locks a workprocess for one single user session for an undetermined period of time. If your Python program "hangs", the workprocess may also hang forever, and if this happens to all workprocesses in the system, the system is dead...

If the requirement is, that the "data extraction process" can be started from external side (Python program) instead from inside SAP (ABAP report), you could refine this scenario as follows:

  1. Instead of a Report with the above code, you write a Function Module (containing the same coding)
  2. Your Python program needs to be Server and Client at the same time:
  3. First you start listening at the RFC Destination as before
  4. Then in a second thread you open a client connection and call the Function Module from step 1
  5. This FM now starts sending the data to the listening RFC Server.
  6. Once it is finished doing that, the FM will return OK in its response
  7. The RFC Client in your program receives the OK and then stops the RFC Server...

There are also other solutions possible, e.g.: ABAP side opens the Cursor, reads the table in chunks, compresses each chunk and stores it in a big XSTRING variable. Once the Cursor is closed it can now send the XSTRING in several chunks over to the external side. Rolling does not matter anymore, all you need to do is store the current index into the XSTRING in a session variable, so it survives several calls (roll-outs and roll-ins).

Best Regards, Ulrich

bsrdjan commented 3 years ago

@DenisTis Python is used as example here, all the same with NodeJS

DenisTis commented 3 years ago

Thanks a lot for your input. I was able to develop same solution as proposed before going in vacations. Thank you for the help!!