Btrieve developer on retainer: TAXUPD opens TAXROLL.BTR with mode 4 Exclusive and exits without Close on validation error — all other users get Btrieve status 85 File Locked
Pervasive PSQL v11.3 • Windows Server 2019 • Btrieve Op 0 mode byte 4 • status 85 File Locked • PCC ghost connection • retainer billing
Hampton County Tax Office ran its property assessment workflow on a legacy Btrieve application that had worked reliably for over fifteen years. The Btrieve engine — Pervasive PSQL v11.3 running in client-server mode on Windows Server 2019 — managed TAXROLL.BTR, the property tax roll file for 12,847 assessed properties. The assessment cycle ran once a quarter: a batch C++ program, TAXUPD.EXE, read the appraisal district feed, validated each property record, and updated the assessed values. During the third-quarter cycle, a tax clerk who opened the tax roll in the property search application got a message she had never seen: Btrieve Error 85: The file is locked by another user.
The error spread to all twelve workstations that accessed TAXROLL.BTR. TAXUPD.EXE had already finished — or rather, it had crashed. But the lock remained. The Btrieve retainer developer was called within the hour.
The TAXUPD program and the Exclusive open
TAXUPD.EXE was a 2003-era C++ program using the Btrieve C API directly via the BTRV() function. It opened TAXROLL.BTR at startup and processed records sequentially. The relevant excerpt from the source code (recovered from version control):
/* Open TAXROLL.BTR in Exclusive mode */
status = BTRV(0, posBlock, &dataLen, dataBuffer, keyBuffer, 4);
if (status != 0) {
fprintf(stderr, "ERROR: Open failed, status %d\n", status);
exit(1);
}
/* Get first record */
status = BTRV(12, posBlock, &dataLen, dataBuffer, keyBuffer, 0);
while (status == 0) {
/* Validate ASSESSED_VALUE field (bytes 48-57, 10 digits) */
if (!isNumeric(dataBuffer + 48, 10)) {
fprintf(stderr, "ERROR: non-numeric ASSESSED_VALUE\n");
exit(1); /* ← exits without BTRV(1,...) Close */
}
/* Parse and update assessed value */
newValue = lookupAppraisalValue(dataBuffer);
memcpy(dataBuffer + 48, newValue, 10);
status = BTRV(3, posBlock, &dataLen, dataBuffer, keyBuffer, 0);
/* Get next record */
status = BTRV(6, posBlock, &dataLen, dataBuffer, keyBuffer, 0);
}
/* Close the file */
BTRV(1, posBlock, &dataLen, dataBuffer, keyBuffer, 0);
printf("TAXROLL updated successfully.\n");
The first BTRV call uses op code 0 (Open) with the last parameter 4 (Exclusive mode). In Pervasive PSQL, opening a file with mode byte 4 grants the opening process exclusive access to the entire file. No other user or application can open the same file while the exclusive open is held. This is appropriate for batch programs that need to prevent all concurrent access during a bulk update. The exclusive open is held until the program issues op code 1 (Close).
The bug: the validation error path on line 12 calls exit(1) without first issuing BTRV(1,...). When the program terminates without closing the file, the Pervasive PSQL MKDE (MicroKernel Database Engine) does not immediately release the exclusive open. The engine waits for the client to explicitly disconnect or for the session timeout to expire. On this network configuration (VPN clients with a 15-minute session timeout), the exclusive lock held for up to fifteen minutes after TAXUPD.EXE had already crashed.
Btrieve status 85 and the Pervasive Control Center diagnosis
The retainer developer opened the Pervasive PSQL Control Center (PCC) on the server and navigated to Monitor → Active Connections. The connection list showed one entry for TAXROLL.BTR: workstation TAXSRV01, user TAXBATCH, file access Exclusive. But Task Manager on TAXSRV01 showed no running TAXUPD.EXE process. The connection was a ghost — an artifact of the engine’s session state machine that still tracked the file open after the client process had terminated.
PCC Monitor → Active Connections:
Workstation: TAXSRV01
User: TAXBATCH
File: C:\PSQL\DATA\TAXROLL.BTR
Access: Exclusive
Open Time: 09:47:23
Last Access: 09:51:14 ← last Op 6 Get Next before crash
PCC Monitor → Files In Use:
TAXROLL.BTR Exclusive 1 user C:\PSQL\DATA\TAXROLL.BTR
The immediate fix was straightforward: in PCC, right-click the ghost connection and select Disconnect Session. The engine dropped the stale exclusive open. Within seconds, all twelve workstations could open TAXROLL.BTR again — Btrieve status 85 cleared immediately. The property search application resumed on all terminals.
But the underlying code problem remained. The next quarter’s assessment run would trigger the same issue the first time TAXUPD.EXE encountered a validation error in the appraisal district feed.
Finding all seven exit paths without Close
The retainer developer audited TAXUPD.EXE’s source code for every exit path that followed a successful BTRV(0,...) open. A successful Op 0 Open means the exclusive lock is held from that point forward. Every subsequent exit(), return from main, or unhandled exception must first call BTRV(1,...).
The audit found seven exit paths. Three had a BTRV(1) close. Four did not:
Line 87: non-numeric ASSESSED_VALUE validation — exit(1) ← missing Close
Line 104: parcel ID length != 10 validation — exit(1) ← missing Close
Line 121: appraisal lookup returned NULL — exit(1) ← missing Close
Line 139: BTRV(3) Update returned non-zero status — exit(1) ← missing Close
Line 156: EOF loop (status == 9) — BTRV(1) present ✓
Line 163: Get Next returned status 64 (lost position) — exit(1) ← missing Close
Line 178: main() return 0 — BTRV(1) present ✓
Line 182: signal handler SIGTERM — exit(0) ← missing Close
The fix added BTRV(1, posBlock, &dataLen, dataBuffer, keyBuffer, 0) before each of the five missing-Close exit paths, and updated the SIGTERM signal handler to call a cleanup function before terminating.
The second change was more significant: the Op 0 mode byte was changed from 4 (Exclusive) to 0 (Normal). Mode 0 opens the file in normal shared mode — multiple users can have the file open simultaneously, with record-level locking for individual update operations. TAXUPD.EXE never required exclusive file-level access. The original developer had used mode 4 from a code template without considering the implications. With mode 0, even if TAXUPD.EXE exits without closing the file, other users are not blocked — they share the file normally. The ghost session at worst holds a stale normal open, which has no blocking effect on reads or updates by other processes.
After the fix: mode 0 behavior verified
To verify, the developer ran a modified test version of TAXUPD.EXE that deliberately triggered the validation error and exited without closing. In PCC, the ghost connection appeared as expected — but with access mode Normal instead of Exclusive. All twelve workstations opened TAXROLL.BTR and queried records without receiving status 85. The mode 0 ghost connection caused no blocking at all.
After production deployment: status 85 File Locked incidents dropped from one or two per quarterly assessment cycle to zero. The PCC Monitor was added to the quarterly assessment runbook: if TAXUPD.EXE fails, check Active Connections before investigating data issues.
The retainer developer also added a code review note to the TAXUPD source: Op 0 mode byte 4 (Exclusive) should be reserved for file restructuring operations (creating indexes, rebuilding the file) where no other access can be tolerated. Batch update programs that use Op 3 (Update) with standard record-level locking should use mode 0 (Normal) or mode 8 (Read-Only for read-only access). The fifteen-year history of the program showed that no concurrent user had ever caused a conflict during a quarterly run — the exclusive mode was always unnecessary.
How HourTab helps Btrieve retainer developers
The investigation took 3 hours. The PCC diagnosis took 20 minutes. The source audit of seven exit paths took 1 hour. The fix and regression test took 1.5 hours. The Btrieve retainer consultant also spent 30 minutes explaining to the tax office IT manager why changing a single number (mode byte 4 to 0) eliminated the entire class of incident going forward. That explanation time is worth logging separately: a Btrieve expert explaining why Exclusive mode should only be used for file restructuring is worth more in long-term maintenance cost than the one-line code fix.
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: Btrieve developer retainers
What does a Btrieve developer on retainer typically do?
A Btrieve developer on monthly retainer covers Btrieve / Pervasive PSQL / Actian PSQL file maintenance (Op 0 Open with correct mode byte — 0 Normal, 4 Exclusive, 8 Read-Only; Op 1 Close on all exit paths including error paths; Op 2 Insert, Op 3 Update, Op 4 Delete with correct key number and key buffer setup; Op 12 Get First and Op 6 Get Next for sequential traversal); Btrieve transaction management (Op 20 Begin Transaction, Op 21 End Transaction, Op 22 Abort Transaction on error paths); Btrieve status code diagnosis (status 84 record in use, status 85 file locked by another user, status 91 connection lost, status 171 invalid name); Pervasive PSQL Control Center (PCC) monitoring including Active Connections, Files In Use, and Monitor Lock Activity; PSQL engine configuration (MKDE timeout, session timeout, max sessions, lock retry settings); and Actian PSQL upgrade path coordination from legacy Pervasive PSQL versions.
What Btrieve debugging work is most commonly underlogged?
Btrieve programs that open a file with Exclusive mode (mode byte 4) and exit on error without issuing Op 1 (Close) are the most systematically underlogged Btrieve retainer work. Pattern: TAXUPD.EXE calls BTRV(0,...,4) — Op 0 Open, mode byte 4 (Exclusive); processes records using Op 12 + Op 6; on validation error → exit(1); no BTRV(1,...) issued; PSQL MKDE holds exclusive file open until session timeout (5–30 minutes); all other users receive status 85; PCC shows ghost connection; fix: BTRV(1,...) before every exit path after successful open; change mode byte 4 → 0 (Normal) if exclusive file-level access was never required. Log must name the mode byte, the error path that skipped Close, PCC ghost connection state, and before/after status 85 frequency.
What are typical Btrieve developer retainer rates?
Entry-level Btrieve developers with Btrieve op code basics, BTRV() C function signature, and Pervasive PSQL Control Center navigation typically bill at $65 to $110 per hour. Mid-level Btrieve developers with status code root cause diagnosis (status 84 vs. 85 distinction, PCC Active Connections ghost session identification, PSQL session timeout configuration, Btrieve transaction Op 20/21/22 lifecycle), multi-file Btrieve application maintenance, and Pervasive PSQL version upgrade coordination typically bill at $95 to $165 per hour. Senior Btrieve developers with Btrieve file rebuild and repair (BUTIL –RECOVER, BUTIL –REBUILD, ACS alternate collating sequence reconfiguration), Btrieve-to-SQL migration (mapping Btrieve key definitions to Actian PSQL DDL, DDF synchronization, dual Btrieve/SQL access maintenance), multi-user lock architecture consulting, and Actian PSQL engine administration typically bill at $140 to $240 per hour. Monthly retainer ranges: $2,000 to $4,500 per month for status code diagnosis, PCC monitoring, and application maintenance; $4,000 to $9,000 per month for Btrieve-to-SQL migration, engine upgrade, and architecture work.
What should a Btrieve developer retainer agreement include?
A Btrieve retainer agreement should specify: Pervasive PSQL or Actian PSQL version and engine topology (workgroup vs. client-server); whether the retainer covers Btrieve C API applications (BTRV() call interface), Btrieve Pascal or COBOL interface applications, or applications using the Btrieve ActiveX/COM object; whether DDF file inspection is in scope (FILE.DDF, FIELD.DDF, INDEX.DDF — required for SQL access, absence or corruption is common in migrated environments); whether Btrieve transaction management is in scope (Op 20/21/22 transaction lifecycle, page lock diagnosis from uncommitted transactions); whether Pervasive PSQL to Actian PSQL migration is in scope; whether file rebuild and repair work is in scope (BUTIL utilities, ACS reconfiguration); and whether PCC engine administration is covered (session monitoring, connection cleanup, engine configuration for lock retry and session timeout).
How should Btrieve developer retainer hours be logged?
Log each Btrieve retainer session with the PSQL version, application name, file name, op code, mode byte, status code observed, PCC state, and before/after metric. For an Exclusive open without Close: Pervasive PSQL v11.3 on Windows Server 2019; application TAXUPD.EXE; file TAXROLL.BTR; BTRV(0,...,4) — Op 0 Open, mode byte 4 (Exclusive); validation error at record 4,293 → exit(1) without BTRV(1,...); PSQL MKDE holds exclusive open; session timeout 15 minutes (VPN); all 12 users receive status 85 for 15 minutes; PCC Active Connections: ghost TAXUPD.EXE Exclusive on TAXROLL.BTR; PCC Disconnect Session → status 85 cleared; audit: 5 of 7 exit paths missing BTRV(1,...); fix: BTRV(1,...) added before all 5 missing exit paths; mode byte changed 4 → 0; status 85: 1–2/quarter → 0; hours: 0.5h PCC diagnosis + 1h source exit-path audit + 1h fix + 0.5h regression + 0.5h IT manager explanation.
See also: RPG / ILE RPG developer retainer · COBOL developer retainer · Db2 developer retainer