jBASE developer on retainer: ORDPRC READL on ORDER-MASTER holds record lock when GOTO skips WRITE and RELEASE on already-picked validation path — warehouse picker gets 30-second lock timeout in ORDPCK — jBASE developer on monthly retainer
jBASE 5.7.4 • RHEL 8.5 • READL without RELEASE • jSHOW LOCKS • GOTO skips non-WRITE exit • lock timeout • retainer billing
At Southwest Beverage Distributors, the warehouse picking confirmation terminal started producing a “Record is locked — try later” error on specific orders. A picker would scan an order barcode to confirm picking, the ORDPCK program would retrieve the order, and instead of confirming, it would pause for thirty seconds and then reject with a lock message. The picker would try again a few minutes later and sometimes succeed, sometimes fail. The pattern was intermittent but reproducible on the same set of order IDs.
The jBASE retainer developer was called in. The diagnosis took 45 minutes. The root cause was a single missing RELEASE statement in the ORDPRC order processing program — a lock acquired by READL on the ORDER-MASTER file was never released when ORDPRC determined an order was already in the “PICKED” status and exited the order loop through a GOTO NEXT_ORDER path that bypassed the normal WRITE and never explicitly called RELEASE.
READL and how jBASE record locks work
In jBASE BASIC, READL acquires an exclusive record-level lock on the specified item before reading it. The lock is held until one of three things happens:
- WRITE — writes the record back to the file and implicitly releases the lock.
- WRITEU — writes the record but does not release the lock (used when you need to hold the lock across multiple writes).
- RELEASE — explicitly releases the lock without writing the record.
Unlike SQL row locks that are automatically released at the end of a transaction or statement, jBASE READL locks have no automatic transaction scope. A lock acquired by READL in a long-running jBASE program persists for the lifetime of that program’s session — or until the program explicitly releases it. If the program exits a code path that needed the lock (validation, inspection, comparison) without writing and without releasing, the lock remains held by the originating port until that port’s session ends.
This is a known pattern in MultiValue environments (jBASE, Universe, UniData, D3, QM, Reality) and is the direct equivalent of an “open file without close” bug in other languages. The difference is that in file I/O, an unclosed file handle is usually harmless for readers — in jBASE READL, an unreleased lock prevents any other program from acquiring a lock on that record, causing lock timeouts for all concurrent users who need to update or lock-read the same item.
The ORDPRC program and the already-picked exit path
The ORDPRC order processing program ran continuously on a shared terminal as the day’s orders were dispatched to the warehouse. For each order, ORDPRC acquired an exclusive lock on the ORDER-MASTER record to prevent two dispatchers from sending the same order simultaneously:
OPEN 'ORDER-MASTER' TO F.ORDERS ELSE STOP 201, 'ORDER-MASTER'
LOOP
INPUT ORD.ID, 8: ;* dispatcher scans order barcode
IF ORD.ID = '' THEN GOTO DONE
READL ORD.REC FROM F.ORDERS, ORD.ID LOCKED
PRINT 'Order ': ORD.ID: ' is locked. Try again.'
GOTO NEXT_ORDER
END ELSE
PRINT 'Order ': ORD.ID: ' not found.'
GOTO NEXT_ORDER
END
* Validate order status
ORD.STATUS = ORD.REC<3> ;* attribute 3 = ORDER_STATUS
IF ORD.STATUS = 'PICKED' THEN
PRINT 'Order ': ORD.ID: ' already picked. Skipping.'
GOTO NEXT_ORDER ;* ← BUG: no RELEASE, no WRITE
END
IF ORD.STATUS = 'CANCELLED' THEN
PRINT 'Order ': ORD.ID: ' cancelled. Skipping.'
GOTO NEXT_ORDER ;* ← BUG: no RELEASE, no WRITE
END
* Dispatch the order
ORD.REC<3> = 'DISPATCHED'
ORD.REC<9> = TIMEDATE() ;* attribute 9 = DISPATCH_TIME
WRITE ORD.REC ON F.ORDERS, ORD.ID ;* releases lock
PRINT 'Order ': ORD.ID: ' dispatched.'
NEXT_ORDER: *
REPEAT
DONE: *
The READL on line 6 acquires an exclusive lock on the ORDER-MASTER record for ORD.ID. When ORD.STATUS = 'PICKED' is true, the program prints a message and jumps to NEXT_ORDER via GOTO NEXT_ORDER. This GOTO skips the WRITE on line 22 — and there is no RELEASE F.ORDERS, ORD.ID anywhere on the PICKED or CANCELLED exit paths. The lock acquired by READL remains held.
The dispatch terminal ran ORDPRC in a loop from 06:00 to 18:00. Any order that was scanned and found to be already-picked had its ORDER-MASTER record locked by the ORDPRC port (port 7, PID 14823) from the moment the dispatcher scanned it until 18:00 when ORDPRC terminated and jBASE released all locks held by that session.
jSHOW LOCKS: identifying the lock holder
The retainer developer ran jSHOW LOCKS from a jSHELL session while the ORDPCK lock timeout was actively occurring:
jSHELL> SHOW-LOCKS
Port PID User File Item ID Time
---- ----- -------- ---------------- ----------- --------
7 14823 DISPATCH ORDER-MASTER ORD004821 09:14:32
7 14823 DISPATCH ORDER-MASTER ORD003977 10:02:18
7 14823 DISPATCH ORDER-MASTER ORD004156 11:47:41
12 18204 PICKER1 ORDER-MASTER ORD006142 14:22:09
Port 7 (the ORDPRC dispatch terminal session, user DISPATCH, PID 14823) held locks on three ORDER-MASTER records. The lock times showed that ORD004821 had been locked since 09:14:32 — over five hours earlier. ORD003977 since 10:02. ORD004156 since 11:47. These were orders that ORDPRC had scanned and found to be already-picked at those times, never released. Port 12 (a ORDPCK picker session) held one lock, which was expected — that was the normal active READL for a picking confirmation in progress.
The developer cross-referenced the locked item IDs against the ORDER-MASTER records. Each had ORDER-STATUS = 'PICKED' — confirming they were the already-picked orders that ORDPRC had scanned and skipped. The ORDPCK lock timeouts occurred because pickers had been trying to rescan those same orders to re-confirm picking (the warehouse system occasionally required re-confirmation for quality checks), and each attempt got the lock timeout from port 7.
Fix: RELEASE before each non-WRITE GOTO
The fix was to add RELEASE F.ORDERS, ORD.ID on every exit path that had a GOTO NEXT_ORDER without a preceding WRITE:
IF ORD.STATUS = 'PICKED' THEN
RELEASE F.ORDERS, ORD.ID ;* ← added
PRINT 'Order ': ORD.ID: ' already picked. Skipping.'
GOTO NEXT_ORDER
END
IF ORD.STATUS = 'CANCELLED' THEN
RELEASE F.ORDERS, ORD.ID ;* ← added
PRINT 'Order ': ORD.ID: ' cancelled. Skipping.'
GOTO NEXT_ORDER
END
The developer reviewed the entire ORDPRC source for every GOTO and RETURN that could be reached after a successful READL without a WRITE. In addition to the PICKED and CANCELLED paths, a third path was found: a data integrity check at line 81 that verified the order’s warehouse zone was in the current dispatcher’s assigned zone list — a mismatch printed “Order outside your zone” and jumped to NEXT_ORDER without releasing. RELEASE F.ORDERS, ORD.ID was added there as well.
After the fix, ORDPRC was recompiled with BASIC BP ORDPRC and recatalogued with CATALOG BP ORDPRC. The ORDPRC session was restarted. The three previously-held locks were gone. A test run scanning an already-picked order confirmed that the READL lock was acquired, the PICKED path was taken, RELEASE executed, and jSHOW LOCKS showed no residual lock on that item. ORDPCK lock timeout incidents dropped to zero.
The READL/RELEASE pattern in jBASE: the full correct form
The correct pattern for any jBASE READL operation is to enumerate every possible exit from the code block that follows the lock acquisition and ensure each one either writes (releasing the lock automatically) or explicitly releases:
READL REC FROM F.FILE, ITEM.ID LOCKED
* Another process holds the lock
PRINT 'Item locked'
GOTO NEXT ;* no RELEASE needed: we never got the lock
END ELSE
* Item doesn't exist
PRINT 'Item not found'
GOTO NEXT ;* no RELEASE needed: we never got the lock
END
* At this point: we hold the lock on ITEM.ID
* Every exit from here must WRITE or RELEASE
IF REC<1> = 'DONE' THEN
RELEASE F.FILE, ITEM.ID ;* exit 1: no update needed
GOTO NEXT
END
IF REC<2> < 0 THEN
RELEASE F.FILE, ITEM.ID ;* exit 2: validation failure
PRINT 'Invalid record'
GOTO NEXT
END
REC<5> = TIMEDATE()
WRITE REC ON F.FILE, ITEM.ID ;* normal exit: WRITE releases lock
NEXT: *
The key discipline: after a successful READL (reaching the code after the LOCKED and ELSE clauses), the developer must treat lock release as a responsibility as explicit as file close in C or connection close in JDBC. jBASE will not do it automatically before the program’s next iteration.
One additional hardening approach used at Southwest Beverage: a post-incident code review of all ORDPRC, ORDPCK, ORDENT, and INVADJ programs identified seven additional READL calls across four programs where at least one exit path lacked a RELEASE. None of those had caused reported incidents (the orders involved were not scanned again during the same session), but they were all fixed preventively. The retainer developer documented each fix with the program name, line number, and the business condition that the missing-RELEASE path handled.
How HourTab helps jBASE retainer developers
The diagnosis and fix took two hours. But the retainer developer also spent thirty minutes walking the distributor’s in-house developer through the READL/WRITE/RELEASE triad and why jBASE locks differ from SQL transaction-scoped row locks. That explanation is what prevents the same bug from recurring in the next program the in-house developer writes, and it is billable retainer work — not a courtesy overhead absorbed into the fix time.
In a HourTab log, the session appears as: “ORDPRC READL lock not released on PICKED/CANCELLED/wrong-zone exit paths: traced via jSHOW LOCKS, 3 orders locked since morning; added RELEASE F.ORDERS,ORD.ID on 3 non-WRITE GOTO paths; preventive audit found 7 more in ORDPCK/ORDENT/INVADJ; READL vs SQL lock semantics walkthrough with dev team, 2.5h.” The warehouse operations manager reads that entry and sees exactly why the lock timeouts were happening and why 2.5 hours of retainer time was the right investment.
HourTab turns a time-tracker CSV into a public retainer-hours URL your client can bookmark — no login, no portal, no “how many hours do I have left?” emails. Free for one retainer. $9/mo for up to ten.
FAQ: jBASE developer retainers
What does a jBASE developer on retainer typically do?
A jBASE developer on monthly retainer covers jBASE BASIC program maintenance (READ/READL/READU; WRITE/WRITEU/RELEASE; OPEN; dynamic array operations with AM/VM/SVM delimiters; LOCATE; GOSUB/RETURN; CALL/RETURN for external subroutines; EXECUTE for shell integration); jBASE lock management (READL lock persistence, jSHOW LOCKS diagnostics, UNLOCK FILE administrative release, lock timeout configuration); jBASE file system maintenance (jRF resize, jSF statistics, jEDIT record browsing, CREATE-FILE); jBASE jQL query maintenance (SELECT, SORT, LIST, WITH, BY, TOTAL, SUBTOTAL, HEADING, FOOTING, dictionary-driven fields and correlatives); jBASE jAgent and RESTful API maintenance; and T-ATT/T-DET sequential file I/O.
What jBASE lock debugging work is most commonly underlogged?
READL without RELEASE on validation-failure GOTO paths is the most systematically underlogged jBASE retainer work. Pattern: READL acquires exclusive lock; validation finds record in unexpected state (already picked, cancelled, wrong zone); GOTO exits processing loop without WRITE or RELEASE; lock persists for entire session; concurrent programs get lock timeout on same item. Diagnosis: jSHOW LOCKS shows item ID and port number of lock holder; fix: RELEASE F.FILE, ITEM.ID before each non-WRITE GOTO. Full code audit required: every GOTO and RETURN reachable after a successful READL must either reach a WRITE or explicitly execute RELEASE.
What are typical jBASE developer retainer rates?
Entry-level jBASE developers with BASIC basics (READ/WRITE/READL/RELEASE, dynamic array operations, OPEN, LOCATE, jQL SELECT/SORT) and basic jSHOW LOCKS diagnostics typically bill at $60 to $100 per hour. Mid-level developers with READL/RELEASE lock pattern debugging (lock persistence, jSHOW LOCKS port analysis, UNLOCK FILE, lock timeout config), CALL/RETURN subroutine architecture, file system maintenance (jRF, jSF, J4/JEDI file type selection), advanced jQL (TOTAL/SUBTOTAL/BREAK-ON, F-correlative cross-file references), and T-ATT sequential I/O typically bill at $88 to $150 per hour. Senior developers with jAgent API architecture, database performance tuning, version migration (jBASE 4.x to 5.7, Universe/UniData to jBASE), TJ journaling configuration, and jBASE SQL layer maintenance typically bill at $135 to $230 per hour. Monthly retainer ranges: $2,000 to $4,500 per month for program maintenance and lock debugging; $4,000 to $9,000 per month for architecture and API integration.
What should a jBASE developer retainer agreement include?
A jBASE retainer agreement should specify: jBASE version and OS; whether jBASE BASIC program maintenance is in scope (READL/RELEASE lock pattern, CALL subroutine catalog management, dynamic array manipulation, EXECUTE shell integration); whether jQL query and dictionary maintenance is in scope (jQL dictionary fields, F-correlatives, TOTAL/SUBTOTAL queries); whether file system maintenance is in scope (jRF resize, file type selection, hash overflow); whether transaction journaling (TJ logging, recovery, TJREADLOG) is in scope; whether jAgent RESTful API maintenance is in scope; and whether applications migrated from other MultiValue platforms (Universe, UniData, D3, QM, Reality) are covered — migration artifacts often require knowledge of both source and target platforms.
How should jBASE developer retainer hours be logged?
Log each jBASE retainer session with the jBASE version, program names, file names, item IDs involved, jSHOW LOCKS output, and the specific GOTO labels where RELEASE was added. For a READL lock-not-released diagnosis: jBASE 5.7.4 RHEL 8.5; ORDPRC F.ORDERS ORDER-MASTER; READL at line 47; validation at line 68 IF ORD.REC<3> = 'PICKED' GOTO NEXT_ORDER — no RELEASE; ORD004821, ORD003977, ORD004156 locked by port 7 PID 14823 since 09:14, 10:02, 11:47; ORDPCK READL: lock timeout 30s on all three; jSHOW LOCKS: FILE ORDER-MASTER ITEM ORD004821 PORT 7; fix: RELEASE F.ORDERS,ORD.ID before GOTO NEXT_ORDER at lines 68, 74, 81; BASIC BP ORDPRC + CATALOG BP ORDPRC; retested: jSHOW LOCKS shows no ORDER-MASTER locks from port 7 after PICKED/CANCELLED/wrong-zone paths; preventive audit found 7 more missing-RELEASE paths in ORDPCK/ORDENT/INVADJ; hours: 0.25h error analysis + 0.5h jSHOW LOCKS + 0.5h source audit + 0.5h fix + 0.25h test + 0.5h dev walkthrough = 2.5h.
See also: Progress 4GL developer retainer · Informix developer retainer · COBOL developer retainer