Phase 2a: 모듈서버 패턴 + 재사용 빌드시스템 (ac 모듈 38서비스 실동작)

- ac_svr(30 매입서비스 advertise) + rc_svr/st_svr, dbio 정적라이브러리, common 라이브러리
- app/build.sh: 모듈 자동발견(app/src/<mod>/<mod>_svr.pgc) → ecpg+buildserver+ndrxconfig 생성
- ac 배치 2종, 스키마 9테이블, 검증: 41서비스 AVAIL, 체인·배치 psql 커밋, prepared_xacts=0

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
forge-bot 2026-07-19 09:37:17 +00:00
parent e6e29e6b5d
commit 05a0ff3e56
28 changed files with 1964 additions and 410 deletions

65
app/src/clients/ac_ops.c Normal file
View file

@ -0,0 +1,65 @@
/*
* ac_ops - exercises several ac_svr ONLINE services (not the chain) under ONE
* global XA transaction, to prove the module server's advertised services work
* and commit. Usage: ac_ops <purchase_id> [merchant] [amount] [bizdate]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include <ubf.h>
#include "acq.fd.h"
static long callsvc(const char *svc, UBFH **b)
{
long rlen = 0;
if (tpcall((char *)svc, (char *)*b, 0L, (char **)b, &rlen, 0L) < 0) {
fprintf(stderr, "tpcall(%s) FAIL: %s\n", svc, tpstrerror(tperrno));
return -1;
}
return 0;
}
int main(int argc, char **argv)
{
long pid = (argc > 1) ? atol(argv[1]) : 1;
const char *merch = (argc > 2) ? argv[2] : "M0001";
long amount = (argc > 3) ? atol(argv[3]) : 1000000;
const char *bizdate = (argc > 4) ? argv[4] : "2026-07-19";
UBFH *b;
long cnt = 0, adj = 0, months = 0, delta = 100;
char st[16] = "";
BFLDLEN l;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
b = (UBFH *)tpalloc("UBF", NULL, 4096);
if (!b) { fprintf(stderr, "tpalloc FAIL\n"); return 1; }
Bchg(b, T_PURCHASE_ID, 0, (char *)&pid, 0L);
Bchg(b, T_MERCHANT, 0, (char *)merch, 0L);
Bchg(b, T_AMOUNT, 0, (char *)&amount, 0L);
Bchg(b, T_BIZDATE, 0, (char *)bizdate, 0L);
Bchg(b, T_DELTA, 0, (char *)&delta, 0L);
Bchg(b, T_INSTALL_N, 0, (char *)(long[]){3}, 0L);
Bchg(b, T_REASON, 0, "OPS_TEST", 0L);
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (callsvc("ACQ_DUPCHK", &b) < 0) { tpabort(0); return 1; }
Bget(b, T_COUNT, 0, (char *)&cnt, 0L);
if (callsvc("ACQ_FEEADJ", &b) < 0) { tpabort(0); return 1; }
Bget(b, T_ADJ, 0, (char *)&adj, 0L);
if (callsvc("ACQ_INSTALL", &b) < 0) { tpabort(0); return 1; }
Bget(b, T_COUNT, 0, (char *)&months, 0L);
if (callsvc("ACQ_STATUS", &b) < 0) { tpabort(0); return 1; }
l = sizeof(st); Bget(b, T_STATUS, 0, st, &l);
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> ac_ops COMMIT: pid=%ld dupchk_count=%ld fee_adj_id=%ld install_rows=%ld status=%s\n",
pid, cnt, adj, months, st);
tpfree((char *)b); tpclose(); tpterm();
return 0;
}

86
app/src/clients/acqdrv.c Normal file
View file

@ -0,0 +1,86 @@
/*
* acqdrv - acquiring driver client.
*
* Starts ONE global transaction (tpbegin), fires the ACQUIRE -> RECONCILE ->
* SETTLE chain via a single tpcall("ACQUIRE"), then tpcommit (or tpabort when
* the 4th arg is "abort", to prove XA rollback).
*
* Usage: acqdrv [merchant] [amount] [bizdate YYYY-MM-DD] [abort]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include <ubf.h>
#include "acq.fd.h"
int main(int argc, char **argv)
{
const char *merch = (argc > 1) ? argv[1] : "M0001";
long amount = (argc > 2) ? atol(argv[2]) : 1000000; /* 10,000.00 */
const char *bizdate = (argc > 3) ? argv[3] : "2026-07-19";
int do_abort = (argc > 4 && strcmp(argv[4], "abort") == 0);
UBFH *b;
long rlen = 0;
long pid = 0, fee = 0, net = 0, sid = 0;
char st[16] = "";
BFLDLEN l;
if (tpinit(NULL) < 0) {
fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno));
return 1;
}
if (tpopen() < 0) {
fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno));
return 1;
}
b = (UBFH *)tpalloc("UBF", NULL, 4096);
if (b == NULL) {
fprintf(stderr, "tpalloc FAIL: %s\n", tpstrerror(tperrno));
return 1;
}
Bchg(b, T_MERCHANT, 0, (char *)merch, 0L);
Bchg(b, T_AMOUNT, 0, (char *)&amount, 0L);
Bchg(b, T_BIZDATE, 0, (char *)bizdate, 0L);
if (tpbegin(60, 0) < 0) {
fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno));
return 1;
}
if (tpcall("ACQUIRE", (char *)b, 0L, (char **)&b, &rlen, 0L) < 0) {
fprintf(stderr, "tpcall(ACQUIRE) FAIL: %s\n", tpstrerror(tperrno));
tpabort(0);
return 1;
}
Bget(b, T_PURCHASE_ID, 0, (char *)&pid, 0L);
Bget(b, T_FEE, 0, (char *)&fee, 0L);
Bget(b, T_NET, 0, (char *)&net, 0L);
Bget(b, T_SETTLE_ID, 0, (char *)&sid, 0L);
l = sizeof(st); Bget(b, T_STATUS, 0, st, &l);
if (do_abort) {
if (tpabort(0) < 0) {
fprintf(stderr, "tpabort FAIL: %s\n", tpstrerror(tperrno));
return 1;
}
printf(">>> ROLLBACK: global tx aborted. purchase_id=%ld should NOT persist "
"(merch=%s amount=%ld)\n", pid, merch, amount);
} else {
if (tpcommit(0) < 0) {
fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno));
tpabort(0);
return 1;
}
printf(">>> COMMIT OK: purchase_id=%ld merch=%s amount=%ld fee=%ld net=%ld "
"settlement_id=%ld status=%s\n", pid, merch, amount, fee, net, sid, st);
}
tpfree((char *)b);
tpclose();
tpterm();
return 0;
}