KICKS

A transaction processing system for CMS & TSO

KICKS KooKbooK recipe

The 251 project – Workload

Define & Implement the Workload part of the script

This is a series of KooKbooK recipes detailing my successful effort to demonstrate over 250 KICKS users sharing VSAM files in the MVS Turnkey under Hercules, with sub-second response time.

At the end of last week’s recipe we ran a script with all 251 terminals and it logged on all 251 users. But only a few dozen were logged on at any one time because the scripted actions finished and logged off as fast as new sessions were started. That’s because the “scripted actions” were pretty trivial (getting in and out of RPF). This week we’ll define more realistic “scripted actions”, which we’ll call the “workload”.

What are the requirements for the workload?

Workload requirements depend on what you’re trying to simulate. What we want to do is measure response time for “more than 250 KICKS users simultaneously accessing vsam files”. Part of defining a workload is in being precise about what you want, and that could be more precise, so let’s redefine it as

“251 KICKS users each, with 30 seconds average wait between key entries, repetitively entering the same series of transactions accessing vsam files, and expecting the same results every time for every user”.

In other words, (1) there will be a fixed set of operations that all the simulated users will be executing; (2) each simulated user will wait 0-60 seconds (average 30 seconds) between receipt of expected results from one operation and pressing enter (or clear, or pf1, etc) for the next; (3) since results are to be repeatable there will be no writes – only direct reads and sequential browses; (4) we want all 251 to be running at once so each user will repeat his script as often as necessary.

Also note that, assuming sub second response is achieved, 251 users each doing something twice a minute means over 500 transactions per minute, or a little over 8 transactions per second.

Transaction Rate = (Number of Users) / ((Response Time) + (Think Time))

Is this “realistic” in any sense? I suppose that depends on what you mean by “realistic”. Certainly real users would not all be doing the same thing, nor doing it over and over, their think time would probably be longer, and so on. So the script could be modified to be more “realistic”. But even if you worked on the script for a couple of years it would never be exactly like what real users do “on a typical day” (and of course the “typical day” concept itself is moving you down the same slippery slope!). At some point you have to ask “is this good (realistic) enough that I believe it will answer the questions that prompted my scripting effort?”. In that sense I’d argue that YES – it is “realistic”. For example: I used it to discover and solve some of the issues in getting over 250 TSO users running on the Turkey; You could (if you wanted) use it to answer the question posed in the previous recipe (which works better, PAGE or SWAP?); With a couple of changes I’ve used it to compare KICKS vrs CICS overhead on z/OS…

Leaving the question of realism to your own further efforts, let’s move on to implementing this workload. We’ll do so by breaking the script into several pieces: one to get logged in to TSO and KICKS, one to wait until all the users are launched, one to execute a series of KICKS transactions, and one to logoff KICKS and TSO.

We also want to monitor MVS while this is going on so that we can see what might be tuned to improve response time. I translated this to a need to obtain at least one MF1[1] report representing the period while all 251 users are running. Since MF1 reports every 15 minutes, all 251 users must be doing their thing for at least 30 minutes to ensure the desire MF1 report will be produced. We will implement this by having the “one to execute a series of KICKS transactions” part of the script repeat until at least 30 minutes have passed.

One to get logged in to TSO and KICKS

The script must be changed to get logged into KICKS after logging on to TSO, and to get a clear screen in KICKS (by pressing the CLEAR key after getting the initial screen). Little more than typing “KICKS PLTPI(KSG1)” (instead of “RPF”), waiting for some characters from the KSG1 screen (instead of RPF’s “-->” then pressing clear. Just a few extra ‘expect’ sequences as you can see in the complete script listing further on.

Note that KSG1 is just a static screen version of the normal KSGM sign on screen. That is, it does not update every few seconds with a new set of colored letters, but just writes a single set of colored letters and waits for input.

This is a good time to talk about a couple of other KICKS specific issues in our workload.

The standard KICKS startup clist allocates files supporting KICKS functions not used in this demonstration.

This includes an auxiliary trace file, the vsam files supporting the temporary storage and intrapartition transient data queues, and the operator id file.

This also includes the 'user' libraries (SKIKLOAD, KIKRPL, COBCOPY, GCCCOPY) the clist creates when they do not already exist.

And it includes the Murach ‘sample’ application's files.

All this allocation activity increases the script's per user login time, so I commented them out to speed up that part of the demo.

Obviously these are small changes, but when you need to get 250 users logged on they add up. I estimate these changes save 5-10 minutes in the stratup time of the script.

One to wait until all the users are launched

There are probably as many ways to synchronize multiple tasks as there are programmers. The method I’ve used in this case is a control file.

When it first starts up the main task creates the file and writes a zero into it. Then when it has launched all the per-user tasks it re-writes the file with a one (and quits).

After logging on, but before commencing their series of repeating KICKS transactions, the per-user tasks each go into a loop waiting the randomized think time (0-60 seconds) and reading the file. They escape this loop and begin their KICKS transactions when the file has a one in it.

One to execute a series of KICKS transactions

I decided to use transactions from the TAC example I distribute with KICKS. The TAC example is a real world system used for some years in a State Department of Labor. It was used as to enter a variety of transactions which were input to a nightly batch process, the ultimate goal being to reconcile an unemployment tax clearing account. It implements add and browse function on a VSAM esds. Usage instructions for the TAC example are in the KICKS Users Guide.

Previous scripts had what to send and what to expect (as a result of sending) embedded in the script itself. But it’s usually a good idea to separate code and data, so this script reads a file, sending and expecting based on the file contents. The file consists of groups of three lines. In each group the first line is what to send (followed by the AID in angle brackets), the second line is what to expect as a result of sending, and the third line is a blank line to make it easy to see the groups.

When the script reads the end of the file it sends a final clear screen, closes the file, checks to see if it’s time to quit, and if not reopens the file and runs the transaction set over again.

Another advantage of this file based approach is that, since there is a single send and expect for the transactions, it’s easier to provide transaction timing, results checking, and error recovery/reporting.

Here is the file (save as thescript.trans). It’s browsing the TAC sample system’s data entry esds.

BTC0<enter>
MASTER MENU
<pf1>
EMPLOYEE AUTOMATIC REFUND MENU
<pf1>
ADD
<clear>
EMPLOYEE AUTOMATIC REFUND MENU
<pf3>
CHANGE
<clear>
EMPLOYEE AUTOMATIC REFUND MENU
<pf4>
DELETE
<clear>
EMPLOYEE AUTOMATIC REFUND MENU
<pf2>
REVIEW
444444444<enter>
44444444
<enter>
888888888
<pf12>
EMPLOYEE AUTOMATIC REFUND MENU
<pf12>
MASTER MENU
<pf12>
DATA ENTRY CONCLUDED

For the script to work the ESDS must contain the right data. If not the script will return unexpected results. The following job loads sufficient and appropriate data.

//TACDATA JOB CLASS=C,MSGCLASS=A,MSGLEVEL=(1,1)
//*
//DELDEF EXEC PGM=IDCAMS,REGION=1024K
//SYSPRINT DD SYSOUT=*
//SYSIN DD *
DELETE HERC01.KICKS.TACDATA
SET MAXCC = 0
DEFINE CLUSTER -
(NAME(HERC01.KICKS.TACDATA) VOLUMES(PUB002) -
TRACKS(15 15) NONINDEXED -
SHAREOPTIONS(1 3) UNIQUE -
RECORDSIZE(204 204) -
) -
DATA ( NAME(KICKS.TACDATA.DATA))
/*
//*
//* LOAD 200 BYTE RECORDS (3 CARDS PER) INTO TACDATA
//*
//LOAD1 EXEC PGM=STKCARDS,PARM='3'
//STEPLIB DD DSN=HERC01.KICKSSYS.V1R5M0.SKIKLOAD,DISP=SHR
//SYSTERM DD SYSOUT=*,DCB=BLKSIZE=120
//SYSPRINT DD DSN=&REPROIN,DISP=(,PASS),UNIT=SYSDA,
// SPACE=(CYL,(1,1)),DCB=(RECFM=FB,LRECL=200,BLKSIZE=2000)
//SYSIN DD *,DCB=BLKSIZE=80
AAR 1111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
AAR 2222222222222222222222222222222222222222222222222222222222222222222222222
22222222222222222222222222222222222222222222222222222222222222222222222222222222
22222222222222222222222222222222222222222222222222222222222222222222222222222222
AAR 4444444444444444444444444444444444444444444444444444444444444444444444444
44444444444444444444444444444444444444444444444444444444444444444444444444444444
44444444444444444444444444444444444444444444444444444444444444444444444444444444
AAR 8888888888888888888888888888888888888888888888888888888888888888888888888
88888888888888888888888888888888888888888888888888888888888888888888888888888888
88888888888888888888888888888888888888888888888888888888888888888888888888888888
AAR 8888888888888888888888888888888888888888888888888888888888888888888888888
88888888888888888888888888888888888888888888888888888888888888888888888888888888
88888888888888888888888888888888888888888888888888888888888888888888888888888888
AAR 1111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111111111111111111111111111111111111111111111111111
/*
//LOAD2 EXEC PGM=IDCAMS
//SYSPRINT DD SYSOUT=*
//TACDATA DD DSN=&REPROIN,DISP=(OLD,DELETE)
//SYSIN DD *
REPRO INFILE(TACDATA) OUTDATASET(HERCO1.KICKS.TACDATA)
/*

Looking at the transaction file and the data you might become concerned that some of the things I’m calling “transactions’ are just pressing PF keys to go from one screen to the next, or even just pressing the clear key to go ‘up’ a level in the app’s menu system.

There’s two ways to look at this.

The 1st is the user’s perspective. The user probably doesn’t think of those menu navigation steps as ‘transactions’ – he’s probably only counting the initial enter for the start browse, and the subsequent enter for the read next.

The 2nd is what would be reported by a monitor or usage accounting system on a real CICS system. Such systems count any entry ending in an AID key as a transaction; and in my judgment reasonably so. Such entry requires CICS to process the input and send a reply of some kind (even if only a keyboard unlock). CICS response time is therefore almost universally quoted including these events as transactions, and I count them in my calculation of “sub second response”.

One more point before we move on to the logoff. Be aware there are a couple of issues with how or what we are ‘expecting’ to confirm the system has responded properly to what we sent it.

One of those issues is physical. The expect program is not looking at the screen image you see, but at the raw datastream inbound to the c3270 program. This isn’t just a 3270 datastream, it is sliced and diced into a tn3270 datastream! In short, sometimes you won’t be able to successfully ‘expect’ something that is obviously on the screen! Try to target things in the first or last line of the screen, and if you must look at something in the middle minimize the length of the string you ‘expect’.

Another issue is more logical. The expect program looks at strings, but 3270 applications deal in screens. Simple expect logic can tell us is something is present, but not where it is (or its color or other highlighting). And of course sometimes those things are important

This demo works because I’ve carefully chosen what to look for in my expect statements. Be mindful of the above issues if you want to change/extend it.

Of course it’s possible to extend expect scripts to handle both of these issues more elegantly than the demo script, but that’s another day’s story…

One to logoff KICKS and TSO

The script must be changed to log off KICKS before logging off TSO. Just a few extra ‘expect’ sequences as you can see in the complete script listing further on.

One to rule them all and in the darkness bind them

Just kidding…

Here is the modified script to run the workload (save as thescript.exp)

#!/usr/bin/expect -f
# This is the 'final' version of the script to drive the 251
# terminals at once with a KICKS workload accessing vsam files.
# Note this script does not 'handle' all possible exception cases...
proc tso_task { who what where } { # userid, password, termid
# initialize constant for think time simulation
set think 30 ;# average user think time in seconds
# initialize variable for response time recording
set DoStuffTime [expr (30 * 60 * 1000)]; # 30 minutes
set trannum 0 ;# total transactions so far
set trantim 0 ;# total trans time so far
set trantimmax -1 ;# longest trans response so far
set trantimmin 99999 ;# shortest trans response so far
# ---------------------------------------------------------------
# establish c3270 connection
# ---------------------------------------------------------------
# suppress terminal output to our screen(s)
log_user 0 ;# 0 to suppress, 1 to see it...
# spawn c3270 (uses special keymap to accomplish 'attn' function)
spawn c3270 -model 3279-2 -once 0$where@localhost:3270
match_max 10000
# send a CLEAR (wakes up hung screen, put netsol on it)
sleep 1; send "\001"; send "c"
# see if we got a netsol screen
set timeout 30
expect {
"Logon ===>" { set which netsol }
"INPUT NOT RECOGNIZED" { set which netsol }
timeout { set which timeout }
}
# if timeout die
if { $which == "timeout" } {
send_user "$where not at netsol\n"
exit
}
# ---------------------------------------------------------------
# logon to tso
# ---------------------------------------------------------------
# at netsol so logon
sleep 1; send "logon $who"; send "\015"
while { $which != "ready" } {
# see what happened to the logon rqst
expect {
"ENTER CURRENT PASSWORD" { set which needpass }
"READY" { set which ready }
"KEY TO CONTINUE" { set which fsibanner }
timeout { set which timeout }
}
# if timeout die
if { $which == "timeout" } {
send_user "$who $where logon failed\n"
exit
}
# tso wants password so give it
if { $which == "needpass"} {
sleep 1; send "$what"; send "\015"
}
# press enter if fsi banner
if { $which == "fsibanner"} {
sleep 2; send "\015"
}
} ;# end of 'while' ( $which != "ready" )
# ---------------------------------------------------------------
# logon to kicks
# ---------------------------------------------------------------
# at tso ready prompt, so go to KICKS
# -- and use a **static** logon screen --
sleep 1; send "kicks pltpi(ksg1)"; send "\015"
while { $which != "kicks" } {
# see what happened to the KICKS rqst
expect {
-ex "\*\*\*" { set which pressenter }
"Press PF1 for help, CLEAR to " { set which kicks }
timeout { set which timeout }
}
# we send enter for pressenter
if { $which == "pressenter" } {
send "\015"
}
# if timeout die
if { $which == "timeout" } {
send_user "$who $where KICKS logon failed\n"
exit
}
} ;# end of 'while' ( $which != "kicks" )
# at KICKS 'ksgm' screen so send clear
sleep 1; send "\001"; send "c"
# ---------------------------------------------------------------
# logged on now, so wait for everyone else to arrive at the party
# ---------------------------------------------------------------
set flag 0
while { $flag == 0 } {
# expect (anything unlikely) to clear buffer
# -- this also causes a wait between the screen clears
expect "asdfasdfasdf"
# check file flag
set logfile [ open "thescript.wait" "r" ]
gets $logfile flag
close $logfile
}
# ---------------------------------------------------------------
# everyone has arrived, start running the workload
# ---------------------------------------------------------------
set d1 [clock clicks -milliseconds]; set d2 0
set pass 0
while { $d2 < $DoStuffTime } {
set tranfile [ open "thescript.trans" "r" ]
set group 0
incr pass
while { ![eof $tranfile] } {
gets $tranfile transend; if { [eof $tranfile] } { break }
gets $tranfile tranexpect; if { [eof $tranfile] } { break }
gets $tranfile tranblank
incr group
sleep [expr (1+$think*2*rand())]
set ts1 ""; set ts2 ""; scan $transend "%1s" ts1
if { $ts1 == "<" } {
set ts1 ""
scan $transend "<%\[^>]>" ts2
} else {
scan $transend "%\[^<]<%\[^>]>" ts1 ts2
}
set ts2 [string tolower $ts2]
send $ts1
# the PF key sequences below derived using the "od -c" technique
# as discussed in expect FAQ (expect.sourceforge.net/FAQ.html)
switch -- $ts2 \
"enter" { send "\015"
} "clear" { send "\001"; send "c"
} "pf1" { send "\033"; send "OP"
} "pf2" { send "\033"; send "OQ"
} "pf3" { send "\033"; send "OR"
} "pf4" { send "\033"; send "OS"
} "pf12" { send "\033"; send "\[24~"
} default {
puts "\n\ninvalid transend aid, $transend, $ts1, $ts2\n\n"
exit
}
expect {
$tranexpect { set which "goodresp" }
timeout { set which "timeout" }
}
# if timeout die
if { $which == "timeout" } {
send_user "$where, $pass, $group, timeout $tranexpect\n"
set which "break"
break ;# try for a normal logout...
}
set t2 [clock clicks -milliseconds]; set t2 [expr ($t2 - $t1)]
if { $t2 < $trantimmin } { set trantimmin $t2 }
if { $t2 > $trantimmax } { set trantimmax $t2 }
set trantim [expr ($trantim + $t2)]
incr trannum
} ;# end of transfile while
close $tranfile
set d2 [clock clicks -milliseconds]; set d2 [expr ($d2 - $d1)]
# check for 'premature' break from DoStuffTime
if { $which == "break" } { break }
send "\001"; send "c"
} ;# end of DoStuffTime while
# ---------------------------------------------------------------
# we did the 1/2 hour's worth, so print results
# ---------------------------------------------------------------
# record response time
set logfile [ open "$where-response.txt" "w" ]
puts -nonewline $logfile [format "%s\t" $where]
puts -nonewline $logfile [format "%.3f\t" [expr {$d2 / 1e3}]]
puts -nonewline $logfile [format "%d\t" $trannum]
puts -nonewline $logfile [format "%.3f\t" [expr {$trantim / 1e3}]]
puts -nonewline $logfile [format "%.3f\t" [expr {$trantimmax / 1e3}]]
puts $logfile [format "%.3f" [expr {$trantimmin / 1e3}]]
close $logfile
# ---------------------------------------------------------------
# logoff KICKS/TSO, shutdown c3270 connection
# ---------------------------------------------------------------
# try hard to get a normal logoff regardless of what has happened.
# -- do 5 reset/clears...
sleep 1; send "\001"; send "r"; send "\001"; send "c";
sleep 1; send "\001"; send "r"; send "\001"; send "c";
sleep 1; send "\001"; send "r"; send "\001"; send "c";
sleep 1; send "\001"; send "r"; send "\001"; send "c";
sleep 1; send "\001"; send "r"; send "\001"; send "c";
sleep 1; send "\001"; send "r"; send "\001"; send "c";
# hopefully now at clear KICKS screen, so do kssf
send "kssf"; send "\015"
while { $which != "loggedoff" } {
# see what happened to the kssf rqst
expect {
"READY" { set which ready }
"===>" { set which loggedoff }
timeout { set which timeout }
}
# we send logoff for ready
if { $which == "ready" } {
send "logoff"; send "\015"
}
# we shutdown the c3270 session when logged off
if { $which == "loggedoff" } {
send "\035"
expect "c3270>" { send "quit\n" }
}
# if timeout die
if { $which == "timeout" } {
send_user "$who $where kicks/tso logoff failed\n"
exit
}
} ;# end of 'while' ( $which != "loggedoff" )
} ;# end of proc tso_task
# ---------- end of subroutine ----- beginning of mainline ----------
# the userids (251)
set thewho {
"kik001" "kik002" "kik003" "kik004" "kik005"
"kik006" "kik007" "kik008" "kik009" "kik010"
"kik011" "kik012" "kik013" "kik014" "kik015"
"kik016" "kik017" "kik018" "kik019" "kik020"
"kik021" "kik022" "kik023" "kik024" "kik025"
"kik026" "kik027" "kik028" "kik029" "kik030"
"kik031" "kik032" "kik033" "kik034" "kik035"
"kik036" "kik037" "kik038" "kik039" "kik040"
"kik041" "kik042" "kik043" "kik044" "kik045"
"kik046" "kik047" "kik048" "kik049" "kik050"
"kik051" "kik052" "kik053" "kik054" "kik055"
"kik056" "kik057" "kik058" "kik059" "kik060"
"kik061" "kik062" "kik063" "kik064" "kik065"
"kik066" "kik067" "kik068" "kik069" "kik070"
"kik071" "kik072" "kik073" "kik074" "kik075"
"kik076" "kik077" "kik078" "kik079" "kik080"
"kik081" "kik082" "kik083" "kik084" "kik085"
"kik086" "kik087" "kik088" "kik089" "kik090"
"kik091" "kik092" "kik093" "kik094" "kik095"
"kik096" "kik097" "kik098" "kik099" "kik100"
"kik101" "kik102" "kik103" "kik104" "kik105"
"kik106" "kik107" "kik108" "kik109" "kik110"
"kik111" "kik112" "kik113" "kik114" "kik115"
"kik116" "kik117" "kik118" "kik119" "kik120"
"kik121" "kik122" "kik123" "kik124" "kik125"
"kik126" "kik127" "kik128" "kik129" "kik130"
"kik131" "kik132" "kik133" "kik134" "kik135"
"kik136" "kik137" "kik138" "kik139" "kik140"
"kik141" "kik142" "kik143" "kik144" "kik145"
"kik146" "kik147" "kik148" "kik149" "kik150"
"kik151" "kik152" "kik153" "kik154" "kik155"
"kik156" "kik157" "kik158" "kik159" "kik160"
"kik161" "kik162" "kik163" "kik164" "kik165"
"kik166" "kik167" "kik168" "kik169" "kik170"
"kik171" "kik172" "kik173" "kik174" "kik175"
"kik176" "kik177" "kik178" "kik179" "kik180"
"kik181" "kik182" "kik183" "kik184" "kik185"
"kik186" "kik187" "kik188" "kik189" "kik190"
"kik191" "kik192" "kik193" "kik194" "kik195"
"kik196" "kik197" "kik198" "kik199" "kik200"
"kik201" "kik202" "kik203" "kik204" "kik205"
"kik206" "kik207" "kik208" "kik209" "kik210"
"kik211" "kik212" "kik213" "kik214" "kik215"
"kik216" "kik217" "kik218" "kik219" "kik220"
"kik221" "kik222" "kik223" "kik224" "kik225"
"kik226" "kik227" "kik228" "kik229" "kik230"
"kik231" "kik232" "kik233" "kik234" "kik235"
"kik236" "kik237" "kik238" "kik239" "kik240"
"kik241" "kik242" "kik243" "kik244" "kik245"
"kik246" "kik247" "kik248" "kik249" "kik250"
"kik251"
}
# the passwords (251)
set thewhat {
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
"" "" "" "" ""
""
}
# the terminal addresses (251)
set thewhere { "0C5" "0C6"
"0C8" "0C9" "0CA" "0CB" "0CC" "0CD" "0CE"
"1C0" "1C1" "1C2" "1C3" "1C4" "1C5" "1C6"
"1C8" "1C9" "1CA" "1CB" "1CC" "1CD" "1CE"
"2C0" "2C1" "2C2" "2C3" "2C4" "2C5" "2C6"
"2C8" "2C9" "2CA" "2CB" "2CC" "2CD" "2CE"
"3C0" "3C1" "3C2" "3C3" "3C4" "3C5" "3C6"
"3C8" "3C9" "3CA" "3CB" "3CC" "3CD" "3CE"
"D00" "D01" "D02" "D03" "D04" "D05" "D06"
"D08" "D09" "D0A" "D0B" "D0C" "D0D" "D0E"
"D10" "D11" "D12" "D13" "D14" "D15" "D16"
"D18" "D19" "D1A" "D1B" "D1C" "D1D" "D1E"
"D20" "D21" "D22" "D23" "D24" "D25" "D26"
"D28" "D29" "D2A" "D2B" "D2C" "D2D" "D2E"
"D30" "D31" "D32" "D33" "D34" "D35" "D36"
"D38" "D39" "D3A" "D3B" "D3C" "D3D" "D3E"
"D40" "D41" "D42" "D43" "D44" "D45" "D46"
"D48" "D49" "D4A" "D4B" "D4C" "D4D" "D4E"
"D50" "D51" "D52" "D53" "D54" "D55" "D56"
"D58" "D59" "D5A" "D5B" "D5C" "D5D" "D5E"
"D60" "D61" "D62" "D63" "D64" "D65" "D66"
"D68" "D69" "D6A" "D6B" "D6C" "D6D" "D6E"
"D70" "D71" "D72" "D73" "D74" "D75" "D76"
"D78" "D79" "D7A" "D7B" "D7C" "D7D" "D7E"
"D80" "D81" "D82" "D83" "D84" "D85" "D86"
"D88" "D89" "D8A" "D8B" "D8C" "D8D" "D8E"
"D90" "D91" "D92" "D93" "D94" "D95" "D96"
"D98" "D99" "D9A" "D9B" "D9C" "D9D" "D9E"
"DA0" "DA1" "DA2" "DA3" "DA4" "DA5" "DA6"
"DA8" "DA9" "DAA" "DAB" "DAC" "DAD" "DAE"
"DB0" "DB1" "DB2" "DB3" "DB4" "DB5" "DB6"
"DB8" "DB9" "DBA" "DBB" "DBC" "DBD" "DBE"
"DC0" "DC1" "DC2" "DC3" "DC4" "DC5" "DC6"
"DC8" "DC9" "DCA" "DCB" "DCC" "DCD" "DCE"
"DD0" "DD1" "DD2" "DD3" "DD4" "DD5" "DD6"
"DD8" "DD9" "DDA" "DDB" "DDC" "DDD" "DDE"
"DE0" "DE1" "DE2" "DE3"
}
set imax [llength $thewhere]
set logfile [ open "thescript.wait" "w" ]
puts $logfile "0"
close $logfile
for { set ii 0 } { $ii < $imax } { incr ii } {
set who [lindex $thewho $ii]
set what [lindex $thewhat $ii]
set where [lindex $thewhere $ii]
if { [fork] } { sleep 5 } else {
tso_task $who $what $where
exit 0
}
}
set logfile [ open "thescript.wait" "w" ]
puts $logfile "1"
close $logfile
exit 0

Let’s run the new script!

Remember to vary all the terminals online first (type “s varyon” on the mvs console), then start the script.

The script should run for about an hour. it should get all the users logged on (about 20 minutes), run the workload (about 30 minutes), and get all the users logged off (5-10 minutes).

Just before publishing this recipe I reran it myself. My environment was KICKS V1R5M0 on Hercules 3.11 in Fedora 21 Linux, running under VMware 9 workstation on a 3.4 ghz desktop machine running the 64 bit version of Windows 7. Here are the first few lines of my consolidated response time files (cat *.txt >resp trick).

Some final thoughts

The MF1 report for this run showed that the CPU was only about 15% busy, so there is lots of headroom to increase the transaction rate and/or the complexity of the transactions.

It’s also likely possible to define and use even more terminals, but I’m not real interested in going there.

One thing to consider before investing time pushing the number of terminals higher – how many users can be ‘supported’ when 256 can be ‘logged on’ at one time? For security reasons most shops logoff CICS users after only a few minutes of inactivity. I’ve worked in shops that had over 1,000 userids active but never had over 100 logged on at once, and I think that ratio is probably pretty normal.

It would be easy to modify my script to get a generic terminal instead of a specific one. Just change the line that reads

spawn c3270 -model 3279-2 -once 0$where@localhost:3270

to

spawn c3270 -model 3279-2 -once localhost:3270

or

spawn c3270 -model 3279-2 -once KICKS@localhost:3270

and Hercules will provide a terminal from the unnamed or KICKS pool (defined in the hercules.conf file we discussed in KooKbooK recipe #8). This would simulate the way most users logon in the real world – they just tn3270 into a host, get whatever terminal session is available, do their thing, then either log off or get logged off when they become inactive. Sometime later the terminal session they were using is reused by the next guy. When the original users want to do something again he sees he got logged off, so he logs on again, gets a different terminal session (probably), and doesn’t care!

My point is, if you can run 250 users at once you can probably ‘support’ thousands of users, and it would not be so hard to extend this demo to prove it.

But it’s your turn. I leave that demo to you!


Copyright © Mike Noel, 2008-2015; last updated 1/19/2015 for KICKS 1.5.0