ibmi-brunch-learn

Announcement

Collapse
No announcement yet.

Compile RPGLE effect

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Compile RPGLE effect

    Hi All,

    Lets say I have one batch job which run continuosly, which has one core program (in RPG III) to receive command from dataqueue, and after checking the dataqueue message then core program will call API program (in RPGLE).
    The API program coded with RETURN (not *INLR=*ON), to keep the file/code in memory (to speed up the process).
    If I change the code for that API program, how can I take the effect of the change without restart the batch job?

    Thank you in advance...

  • #2
    How is the RPG IV program compiled? With DFTACTGRP(*YES) or *NO? The answers may lead to different solutions. If *NO then what Activation group does the program run in?

    Can the RPG III <shudder> program be modified? In which case the first thing I would do is the trivial task of converting it to RPG IV - then you have many more options. If it can be modified but not converted (don't know why you couldn't but ...) you could change the call to use a variable name rather than a literal. That allows you to signal (probably via the DQ) that the program has been replaced and the target program should be temporarily changed to a No-op one and then back again. This will force a re-resolve and give you the new program.

    Of course if you have multiple instances of the batch job running things will be a bit different but the basic principal is the same.

    Comment


    • #3
      Hi Jon,

      Sorry for late reply.
      Mostly compile with DFTACTGRP(*YES).
      The RPG III pgm can be modified.
      For your suggestion using variable name, currently some already use table to identify the program name that should be called depend on the dataqueue message.
      But some still using hardcoded.

      Comment


      • #4
        OK. I'll quickly explain the mechanics used by the compiler and how you can take advantage of it.

        When a variable is used as the program target, the compiler generated code compares the last value used with the current one. If they are the same then the program pointer is not resolved and the pointer will be used as-is. That means that the "old" version of the program will keep being called until either a) the program ends and restarts, or b) A different program name is passed.

        The normal approach to your problem (for old-style RPG code) is to add a mechanism that allows you to force the reset of the pointer by switching to a different program name (it doesn't even have to exist) and then immediately switching back to the real one. This will cause the pointer to be set to the new version of the program and all is well.

        As to how to trigger this - I have seen many different approaches and it really depends on how the program is run. For example the program could use a data area (or user space) in QTemp so that it is associated with just the one job and when a new version of the program is put in production you set this to a value that indicates a reset is needed. In the program that does the call you test this value before each CALL and initiate the switch if requested. Pseudo code looks like this:

        Get data area value

        If Switch
        store current value as new data area value
        call using dummy value
        reset real value
        End

        call using real value

        Because you are calling a non-existent (or a do nothing) program, no change to the called program is needed.

        If the job is a never ending server job with multiple instances you need to be a bit more elaborate in the reset process but this is the basic approach.

        Needless to say _all_ programs making the calls must be changed to use this approach if you need the solution to be complete. In some cases it may not be needed - for example if the call takes place only once during a batch run - or under circumstances where a RLRSC is being done (this will only affect RPG III/RPG/400 code).

        Hope this helps.

        Comment


        • #5
          Thank you Jon for your explanation. So, for OPM, the key point is to resolve the program pointer by changing the program target variable because the system compare the last variable value with the current one before resolving the program pointer.
          But, how is the approach for DFTACTGRP(*NO)?

          Comment


          • #6
            You have the key point yes.

            "Real" ILE programs i.e. DFTACTGRP(*NO) have the additional option of being cleaned up by reclaiming the activation group in which they are running. Note though that the one you need to clean up is the _caller_. so it works like this:

            If A calls B calls C (where C is the one we need to force resolution on) then A needs to be modified to detect the need for B to re-resolve and when that requirement arises A issues a RCLACTGRP( groupInWhichBisRunning ) prior to calling B.

            It is just an additional option - the method outlined earlier will also work.

            Comment


            • #7
              You could have a special mod library higher up in the *LIBL that is for promoting program changes during business hours. Then the called program in the normal production program library could increment a CALL counter and when it hits some value, say 1000, it sets on *INLR before RETURNing. Then the very next call to that program name will be found in the mod library. You do have to move objects from the mod library to the production library during off hours like on a Sunday morning.

              Ringer

              Comment


              • #8
                The only thing that would save you though Chris is not having to place the new code in the production library. Not sure it helps much does it? And don't forget that a NEP/Server job may take days or weeks to reach the count threshold if that particular instance doesn't get much traffic.

                Comment


                • #9
                  Hi Jon,
                  This allows you to install program changes during production hours without asking users to sign off. New jobs would get the newer objects higher in the *LIBL. If a few users (department) need the newer object, they sign off, on. If it's a program with limited use, just MOVOBJ to another library outside the *LIBL while in use, and all jobs currently using that *PGM will automagically will point to that different library and then install new *PGM to the production library. These are just more blades in the Swiss army knife. Your mileage may vary.
                  Chris

                  Comment


                  • #10
                    But the OP said "Lets say I have one batch job which run continuously" - so sign off was not an option. Sign off would work anyway regardless of whether the new version was placed in production or in a pre-stage library like you suggest. The whole idea of QRPLOBJ is to allow people currently using a program to continue to use the same program object even after it has been replaced - so the only time a pre-stage is essential is if you want one or two specific users to get the new version but not everyone.

                    What am I missing Chris?

                    Comment


                    • #11
                      Sorry Jon, I lost sight of the requirements.

                      I code my DQ programs so I can send a '*SHUTDOWN' string to them. Perhaps Alex could code a '*RESTART' string so the RPG 3 pgm tells the APIs to just restart.

                      // In API: just close down and exit
                      If ( %Parms = 0 ) ;
                      *InLR = *On ;
                      Return ;
                      EndIf ;

                      Comment


                      • #12
                        I know that feeling well Chris! I agree a specific entry as a parameter, or value on a DQ works well. You could use semaphores as well if you wanted to get cutesy. The biggest problem usually is with NEPs running behind web jobs etc. where it is much harder to target a specific instance. In those cases I've had to resort to using a program version level in (in my case) an indexed User Space that records the current program version - then the program (which is always mapped to the space) does a simple "Is my version that same as the current version" test and sets the reinitialization in progress if not.

                        Comment


                        • #13
                          Hi Jon and CRinger400,


                          This post will be long, please be patient.
                          I did some of experiments :
                          I create 2 RPGLE programs with DFTACTGRP(*YES).
                          - The first one (RPGM000R) will call RPGM601R with variable name (PGMNAME) for every 30 seconds (loop forever).
                          If the call is error, RPGM000R will change the variable name to ?DUMMY1?, and will call that again, the call for DUMMY1 will obviously error, and RPGM000R will change the variable name again to ?RPGM601R?, and try to call the variable name again.
                          The program also write to log file with words ?DELAY?..?

                          - RPGM601R will only write to log file for some dummy variable.


                          RPGM000R code is like this :
                          FRLOG0P IF A E DISK
                          *
                          d p_cmdexc pr extpgm('QCMDEXC')
                          d cmddta 1000 options(*varsize)
                          d cmdlen 15 5
                          *
                          d cmddtax s 1000
                          d cmdlenx s 15 5
                          d pgmname s 10 INZ('RPGM601R')
                          *
                          c dow *inlr='0'
                          c call pgmname 77
                          c if *in77='1'
                          c eval log='Failed call pgm ' + pgmname
                          c write RLOG
                          c eval pgmname='DUMMY1'
                          c call pgmname 77
                          c if *in77='1'
                          c eval log='Failed call pgm ' + pgmname
                          c write RLOG
                          c eval pgmname='RPGM601R'
                          c call pgmname 77
                          c if *in77='1'
                          c eval log='Failed call pgm ' + pgmname
                          c write RLOG
                          c else
                          c eval log='Succeed call pgm ' + pgmname
                          c write RLOG
                          c endif
                          c endif
                          c endif
                          /free
                          cmddtax='DLYJOB DLY(30)';
                          cmdlenx=%len(cmddtax);
                          Monitor;
                          p_cmdexc(cmddtax:cmdlenx);
                          On-Error;
                          EndMon;
                          /end-free
                          c eval log='DELAY......................'
                          c write RLOG
                          c enddo
                          c eval *inlr='1'




                          RPGM601R code is like this :
                          FRLOG0P IF A E DISK
                          *
                          d p_cmdexc pr extpgm('QCMDEXC')
                          d cmddta 1000 options(*varsize)
                          d cmdlen 15 5
                          *
                          d cmddtax s 1000
                          d cmdlenx s 15 5
                          d pgmname s 10 INZ('RPGM601R')
                          *
                          c movel '1' ok 1
                          c eval log='OK='+ok
                          c write rlog
                          /free
                          cmddtax='DLYJOB DLY(20)';
                          cmdlenx=%len(cmddtax);
                          Monitor;
                          p_cmdexc(cmddtax:cmdlenx);
                          On-Error;
                          EndMon;
                          /end-free
                          c return




                          The test :
                          - I submit the RPGM000R to QBATCH
                          This is the capture of the call stack :
                          Display Call Stack
                          System: CN770DEV
                          Job: TESTING01 User: SCRANDY Number: 957945
                          Thread: 00000021


                          Type Program Statement Procedure
                          1 QCMD QSYS /01C8
                          RPGM000R ASITEMP _QRNP_PEP_RPGM000R
                          RPGM000R ASITEMP 14 RPGM000R
                          RPGM601R ASITEMP _QRNP_PEP_RPGM601R
                          RPGM601R ASITEMP 20 RPGM601R
                          QCMDEXC QSYS /012F
                          QWCDLYJB QSYS /0035


                          Here is the capture of the log file :
                          File . . . . . . : RLOG0P Library . . . . : ASITEMP
                          Member . . . . . : RLOG0P Record . . . . . : 38
                          Control . . . . . +1 Column . . . . . : 1
                          Find . . . . . . .
                          *...+....1....+....2....+....3....+....4....+....5 ....+....6....+....7.
                          OK=1
                          DELAY......................
                          OK=1
                          DELAY......................
                          OK=1



                          - then I try to change the RPGM601R and recompile it again.
                          The code change only :
                          c movel '2' ok 1

                          This is the capture of the call stack :
                          Display Call Stack
                          System: CN770DEV
                          Job: TESTING01 User: SCRANDY Number: 957945
                          Thread: 00000021


                          Type Program Statement Procedure
                          1 QCMD QSYS /01C8
                          RPGM000R ASITEMP _QRNP_PEP_RPGM000R
                          RPGM000R ASITEMP 14 RPGM000R
                          QF7269C5C4 QRPLOBJ _QRNP_PEP_RPGM601R
                          QF7269C5C4 QRPLOBJ 20 RPGM601R
                          QCMDEXC QSYS /012F
                          QWCDLYJB QSYS /0035


                          Here is the capture of the log file :
                          File . . . . . . : RLOG0P Library . . . . : ASITEMP
                          Member . . . . . : RLOG0P Record . . . . . : 38
                          Control . . . . . 38 Column . . . . . : 1
                          Find . . . . . . .
                          *...+....1....+....2....+....3....+....4....+....5 ....+....6....+....7....+
                          OK=1
                          DELAY......................
                          OK=1
                          DELAY......................
                          OK=1
                          DELAY......................
                          OK=1
                          DELAY......................
                          OK=1
                          DELAY......................
                          OK=1
                          DELAY......................
                          OK=1


                          Now I try to delete the temporary object from QRPLOBJ library
                          This is the capture of the log file :
                          File . . . . . . : RLOG0P Library . . . . : ASITEMP
                          Member . . . . . : RLOG0P Record . . . . . : 53
                          Control . . . . . +1 Column . . . . . : 1
                          Find . . . . . . .
                          *...+....1....+....2....+....3....+....4....+....5 ....+....6....+....
                          DELAY......................
                          Failed call pgm RPGM601R
                          Failed call pgm DUMMY1
                          Failed call pgm RPGM601R
                          DELAY......................
                          Failed call pgm RPGM601R
                          Failed call pgm DUMMY1
                          Failed call pgm RPGM601R


                          So, I can?t get the change of RPGM601R after recompile.

                          Any idea?
                          And also I want to ask, when will the temporary objek in QRPLOBJ will be purged? Because some times in Prod env, the job is in MSGW because the program to be called is missing without we doing anything to QRPLOBJ library.

                          Comment


                          • #14
                            Put a CL program between the main program and the called program.
                            This means that RPGM000R calls CLPGM601C that calls RPGM601R

                            This should do it.

                            Regards
                            Peder

                            Comment


                            • #15
                              At a quick look it should have worked as-is - but I suggest you change the "dummy" called program to a do-nothing one that actually exists. It appears that if the call fails the program pointer is not being changed.

                              Is this an old program you are working with? It seems a very odd mixture of RPG III (Call with indicator - MOVEL) and RPG IV (Monitor).

                              Comment

                              Working...
                              X