I had duplicate Postgres base signatures, and I needed to diagnose the reasons why it was failing to update. The following are helpful for diagnosing functions:
References
StackOverflow DROP FUNCTION without knowing the number/type of parameters?
I had duplicate Postgres base signatures, and I needed to diagnose the reasons why it was failing to update. The following are helpful for diagnosing functions:
References
StackOverflow DROP FUNCTION without knowing the number/type of parameters?
When I wanted to deploy my key to openpgp, I hit the issue where it said No keyserver available:
$ gpg --verbose --keyserver hkps://keys.openpgp.org --send-keys KEYDYID
gpg: Note: RFC4880bis features are enabled.
gpg: sending key KEYDYID to hkps://keys.openpgp.org
gpg: keyserver send failed: No keyserver available
gpg: keyserver send failed: No keyserver available
If you hit this, you can ps -ef dirmgr and then kill -9 the pid for the dirmngr.
Restart the dirmngr --debug-all --daemon --standard-resolver
Check the output for any errors (in my case a TLS issue – TLS connection authentication failed: General error)
09:02:27-homedir@machine:~$ dirmngr --debug-all --daemon --standard-resolver
dirmngr[58503]: reading options from '/Users/homedir/.gnupg/dirmngr.conf'
dirmngr[58503]: reading options from '[cmdline]'
dirmngr[58503]: enabled debug flags: x509 crypto memory cache memstat hashing ipc dns network lookup extprog
dirmngr[58503]: listening on socket '/Users/homedir/.gnupg/S.dirmngr'
DIRMNGR_INFO=/Users/homedir/.gnupg/S.dirmngr:58504:1; export DIRMNGR_INFO;
09:02:52-homedir@machine:~$ dirmngr[58504.0]: permanently loaded certificates: 133
dirmngr[58504.0]: runtime cached certificates: 0
dirmngr[58504.0]: trusted certificates: 133 (132,0,0,1)
dirmngr[58504.4]: handler for fd 4 started
dirmngr[58504.4]: DBG: chan_4 -> # Home: /Users/homedir/.gnupg
dirmngr[58504.4]: DBG: chan_4 -> # Config: /Users/homedir/.gnupg/dirmngr.conf
dirmngr[58504.4]: DBG: chan_4 -> OK Dirmngr 2.3.1 at your service
dirmngr[58504.4]: DBG: END Certificate
dirmngr[58504.4]: DBG: BEGIN Certificate 'server[2]':
dirmngr[58504.4]: DBG: serial: 4001772137D4E942B8EE76AA3C640AB7
dirmngr[58504.4]: DBG: notBefore: 2021-01-20 19:14:03
dirmngr[58504.4]: DBG: notAfter: 2024-09-30 18:14:03
dirmngr[58504.4]: DBG: issuer: CN=DST Root CA X3,O=Digital Signature Trust Co.
dirmngr[58504.4]: DBG: subject: CN=ISRG Root X1,O=Internet Security Research Group,C=US
dirmngr[58504.4]: DBG: hash algo: 1.2.840.113549.1.1.11
dirmngr[58504.4]: DBG: SHA1 fingerprint: 933C6DDEE95C9C41A40F9F50493D82BE03AD87BF
dirmngr[58504.4]: DBG: END Certificate
dirmngr[58504.4]: TLS connection authentication failed: General error
dirmngr[58504.4]: error connecting to 'http://keys.openpgp.org:80': General error
dirmngr[58504.4]: command 'KS_PUT' failed: General error <Unspecified source>
dirmngr[58504.4]: DBG: chan_4 -> ERR 1 General error <Unspecified source>
dirmngr[58504.4]: DBG: chan_4 <- BYE
dirmngr[58504.4]: DBG: chan_4 -> OK closing connection
dirmngr[58504.4]: handler for fd 4 terminated
Create the file ~/.gnupg/dirmngr.conf with the following contents
keyserver hkps://keys.openpgp.org
hkp-cacert /Users/homedir/.gnupg/my.pem
Download the openpgp ceritifcate
echo "" | openssl s_client -showcerts -prexit -connect keys.openpgp.org:443 2> /dev/null \
| sed -n -e '/BEGIN CERTIFICATE/,/END CERTIFICATE/ p' > ~/.gnupg/my.pem
Restart the dirmngr
Re-execute the gpg command
09:02:58-paulbastide@pauls-mbp:~$ gpg --keyserver hkp://keys.openpgp.org:80 --send-keys KEYDYID
gpg: sending key KEYDYID to hkp://keys.openpgp.org:80
Thanks to https://gist.github.com/troyfontaine/18c9146295168ee9ca2b30c00bd1b41e for the assist.
The IBM FHIR Server supports many different persistence stores – Apache Derby, Postgres, and IBM Db2. The IBM Db2 offering includes a multi-tenancy feature with row-level permissions. Each tenant is assigned a unique internal id for the tenantName and tenantKey. The tenantKey on each JDBC connection is verified using a one-way hash, and supports allocating a tenant, adding a key to an existing tenant, revoking a key and revoking all tenant keys.
--update-schema, you can allocate a tenant.java -jar fhir-persistence-schema-*-cli.jar \
--prop db.host=db \
--prop db.port=50000 \
--prop db.database=fhirdb \
--prop user=db2inst1 \
--prop password=change-password \
--db-type db2 \
--allocate-tenant acme32
2021-06-22 11:19:13.226 00000001 INFO .common.JdbcConnectionProvider Opening connection to database: jdbc:db2://db:50000/fhirdb
2021-06-22 11:19:14.346 00000001 WARNING ls.pool.PoolConnectionProvider Get connection took 1.120 seconds
...
2021-06-22 11:22:59.846 00000001 INFO com.ibm.fhir.schema.app.Main tenantId [29] is being pre-populated with lookup table data.
2021-06-22 11:22:59.930 00000001 INFO com.ibm.fhir.schema.app.Main Finished prepopulating the resource type and search parameter code/name tables tables
2021-06-22 11:22:59.948 00000001 INFO com.ibm.fhir.schema.app.Main Allocated tenant: acme32 [key=UsBglbyMJpSi/RjXwrkp0Bj2bAljUI+MixfAikdrcN0=] with Id = 29
2021-06-22 11:22:59.949 00000001 INFO com.ibm.fhir.schema.app.Main The tenantKey JSON follows:
{"tenantKey": "UsBglbyMJpSi/RjXwrkp0Bj2bAljUI+MixfAikdrcN0="}
2021-06-22 11:22:59.949 00000001 INFO com.ibm.fhir.schema.app.Main Processing took: 226.765 s
2021-06-22 11:22:59.950 00000001 INFO com.ibm.fhir.schema.app.Main SCHEMA CHANGE: OK
java -jar fhir-persistence-schema-*-cli.jar \
--prop db.host=db \
--prop db.port=50000 \
--prop db.database=fhirdb \
--prop user=db2inst1 \
--prop password=change-password \
--db-type db2 \
--add-tenant-key acme32
2021-06-22 11:25:53.254 00000001 INFO .common.JdbcConnectionProvider Opening connection to database: jdbc:db2://db:50000/fhirdb
2021-06-22 11:25:54.197 00000001 INFO com.ibm.fhir.schema.app.Main New tenant key: acme32 [key=UVFuDqD/V3v8d9S/XRjRQNu9eFTniksvxgIBbI6mEkg=]
2021-06-22 11:25:54.201 00000001 INFO com.ibm.fhir.schema.app.Main Processing took: 0.975 s
2021-06-22 11:25:54.202 00000001 INFO com.ibm.fhir.schema.app.Main SCHEMA CHANGE: OK
java -jar fhir-persistence-schema-*-cli.jar \
--prop db.host=db \
--prop db.port=50000 \
--prop db.database=fhirdb \
--prop user=db2inst1 \
--prop password=change-password \
--db-type db2 \
--list-tenants
2021-06-22 11:28:43.566 00000001 INFO .common.JdbcConnectionProvider Opening connection to database: jdbc:db2://db:50000/fhirdb
TenantId Status TenantName Schema
29 ALLOCATED acme32 FHIRDATA
2021-06-22 11:28:44.395 00000001 INFO com.ibm.fhir.schema.app.Main Processing took: 0.854 s
2021-06-22 11:28:44.396 00000001 INFO com.ibm.fhir.schema.app.Main SCHEMA CHANGE: OK
java -jar fhir-persistence-schema-*-cli.jar \
--prop db.host=db \
--prop db.port=50000 \
--prop db.database=fhirdb \
--prop user=db2inst1 \
--prop password=change-password \
--db-type db2 \
--revoke-tenant-key acme32 \
--tenant-key UVFuDqD/V3v8d9S/XRjRQNu9eFTniksvxgIBbI6mEkg=
2021-06-22 11:27:17.280 00000001 INFO .common.JdbcConnectionProvider Opening connection to database: jdbc:db2://db:50000/fhirdb
2021-06-22 11:27:18.112 00000001 INFO com.ibm.fhir.schema.app.Main Tenant Key revoked for 'acme32' total removed=[1]
2021-06-22 11:27:18.119 00000001 INFO com.ibm.fhir.schema.app.Main Processing took: 0.867 s
2021-06-22 11:27:18.120 00000001 INFO com.ibm.fhir.schema.app.Main SCHEMA CHANGE: OK
You’ll see one was removed.
java -jar fhir-persistence-schema-*-cli.jar \
--prop db.host=db \
--prop db.port=50000 \
--prop db.database=fhirdb \
--prop user=db2inst1 \
--prop password=change-password \
--db-type db2 \
--add-tenant-key acme32
2021-06-22 11:25:53.254 00000001 INFO .common.JdbcConnectionProvider Opening connection to database: jdbc:db2://db:50000/fhirdb
2021-06-22 11:25:54.197 00000001 INFO com.ibm.fhir.schema.app.Main New tenant key: acme32 [key=UVFuDqD/V3v8d9S/XRjRQNu9eFTniksvxgIBbI6mEkg=]
2021-06-22 11:25:54.201 00000001 INFO com.ibm.fhir.schema.app.Main Processing took: 0.975 s
2021-06-22 11:25:54.202 00000001 INFO com.ibm.fhir.schema.app.Main SCHEMA CHANGE: OK
java -jar fhir-persistence-schema-*-cli.jar \
--prop db.host=db \
--prop db.port=50000 \
--prop db.database=fhirdb \
--prop user=db2inst1 \
--prop password=change-password \
--db-type db2 \
--revoke-all-tenant-keys acme32
2021-06-22 11:32:34.061 00000001 INFO .common.JdbcConnectionProvider Opening connection to database: jdbc:db2://db:50000/fhirdb
2021-06-22 11:32:35.112 00000001 INFO com.ibm.fhir.schema.app.Main Tenant Key revoked for 'acme32' total removed=[2]
2021-06-22 11:32:35.144 00000001 INFO com.ibm.fhir.schema.app.Main Processing took: 1.116 s
2021-06-22 11:32:35.146 00000001 INFO com.ibm.fhir.schema.app.Main SCHEMA CHANGE: OK
You’ll see that two are removed.
You now know the lifecycle for the IBM FHIR Server tenantKey – allocate, add and revoke.
Reference
The IBM FHIR Server supports hard delete using the custom FHIR Operation Framework. The operation is called $erase; you can always read the design document.
The operation supports:
By default, the $erase operation is not enabled, and must be set to true, and the roles allowed to execute the operation, can be tweaked to FHIROperationAdmin or FHIRUsers.
| Property | Type | Description |
fhirServer/operations/erase/enabled | boolean | Enables the $erase operation |
fhirServer/operations/erase/allowedRoles | list | The list of allowed roles, allowed entries are: FHIRUsers every authenticated user, FHIROperationAdmin which is authenticated FHIRAdmin users |
The fhir-server-config.json should be amended with a snippet like the following:
"operations": {
"erase": {
"enabled": true,
"allowedRoles": [
"FHIROperationAdmin",
"FHIRUsers"
]
}
}
As the $erase operation uses stored procedures for Db2 and functions for Postgres, the data schema must be updated to the 4.8.3 level. It’s also worth noting that Derby is also supported as it uses the same DAO as the Db2 and Postgres to run.
A tip, you should search for the references to the Patient’s ResourceId, and delete the referenced resources in a Transaction Bundle. Net – Search, Assemble Bundle, Execute Batch.
Let me show you how to run with it, and operate the $erase operation with a container (Docker: ibmcom/ibm-fhir-server).
Recipe
2. Start the Docker container, and capture the container id. It’s going to take a few moments to start up as it lays down the test database.
docker run -d -p 9443:9443 -e BOOTSTRAP_DB=true -v $(pwd)/fhir-server-config.json:/config/config/default/fhir-server-config.json ibmcom/ibm-fhir-server
cde61943964464b528eb77d132fd2a4952e0eaf43588da1c9b6bb2fa584f0608
3. Check the logs until you see:
docker logs cde61943964464b528eb77d132fd2a4952e0eaf43588da1c9b6bb2fa584f0608
...
[6/16/21, 15:31:34:533 UTC] 0000002a FeatureManage A CWWKF0011I: The defaultServer server is ready to run a smarter planet. The defaultServer server started in 17.665 seconds.
4. Download the Postman Collection for Version Specific Erase and All Versions Erase.
5. Import the Postman Collections and Run

You’ve seen how to use the $erase operation, and a bit more details on configuring and using it.
Postman Collections
The IBM FHIR Server has support for exporting and importing Bulk Data using extended operations for Bulk Data $import, $export and $bulkdata-status, which are implemented as Java Maven projects. The IBM FHIR Server uses JSR252 JavaBatch jobs running in the Open Liberty Java Batch Framework.
The IBM FHIR Server supports storage providers S3 (aws-s3,ibm-cos), File System (file), and now Azure (azure-blob).
I recently implemented support for the Azure Blob Service in IBM/FHIR Pull Request #2413. The code that supports the Azure client uses the OK Http library and not Netty to communicate with the backend. The storage provider is implemented as AzureProvider, and uses the Java – AppendBlobClient to facilitate export. To facilitate import, the code uses the BlobClient to read from a location in 10Kb blocks, and reassemble when a full block is reached. Importantly, this is done in a way to ensure full resources are returned up to a 2Gb limit.
Overview Video
If you want to use the IBM FHIR Server’s bulk data feature with the IBM FHIR Server, use the following recipe:
Recipe – Azure Blob and Bulk Data
1. Login to the Azure Portal
2. Click Home

3. Navigate the Azure services, and click on Storage Accounts.
4. Click Create
5. Enter the appropriate subscription and resource group
6. Give it a name such as fhirintegrationit

7. Click Review+Create

8. Click Create
You’ll see Deployment in Progress for a period of time.

Once it is done, you see Your deployment is complete, proceed to the next steps.
9. Click Go to Resource
10. Click on Access Key

11. Click Show keys and copy the key1’s Connection string. It’ll look like DefaultEndpointsProtocol=https;AccountName=fhirintegrationit;AccountKey=HIDDEN==;EndpointSuffix=core.windows.net

12. Click on Data > Containers

13. Click +Container

14. Fill in the container details and call it bulkdata

14. Click on the bulkdata container
15. Upload blob Data – AllergyIntolerance.ndjson

You should see the file uploaded and available.

You are now ready to use this with the IBM FHIR Server from the main branch. If you need to clone the repo go to IBM/FHIR.
16. Build the Maven Projects and the Docker Build. You should see “[INFO] BUILD SUCCESS” after each Maven build, and “=> naming to docker.io/ibmcom/ibm-fhir-server:latest” when the Docker build is successful.
mvn clean install -f fhir-examples -B -DskipTests -ntp
mvn clean install -f fhir-parent -B -DskipTests -ntp
docker build -t ibmcom/ibm-fhir-server:latest fhir-install
17. Download the fhir-server-config.json
18. Open the fhir-server-config.json and update fhirServer/bulkdata/storageProviders/default entry to be the connection is updated to your Connection string from above.
17. Start the Docker container, and capture the container id. It’s going to take a few moments to start up as it lays down the test database.
docker run -d -p 9443:9443 -e BOOTSTRAP_DB=true -v $(pwd)/fhir-server-config.json:/config/config/default/fhir-server-config.json ibmcom/ibm-fhir-server
cde61943964464b528eb77d132fd2a4952e0eaf43588da1c9b6bb2fa584f0608
18. Check the logs until you see:
docker logs cde61943964464b528eb77d132fd2a4952e0eaf43588da1c9b6bb2fa584f0608
...
[6/16/21, 15:31:34:533 UTC] 0000002a FeatureManage A CWWKF0011I: The defaultServer server is ready to run a smarter planet. The defaultServer server started in 17.665 seconds.
19. Create an $import request and capture the content-location header.
20. Check and poll until you get response code 200 (you should only get 202 in the interim) until you see (note 99 failed validation which is expected):
curl -v -k --user 'fhiruser:change-password' 'https://localhost:9443/fhir-server/api/v4/$bulkdata-status?job=UKt4ESCnqOvAfxYWhdsfUg'
< HTTP/2 200
< content-type: application/json
< date: Wed, 16 Jun 2021 16:01:54 GMT
< content-language: en-US
< content-length: 501
<
{
"transactionTime": "2021-06-16T15:59:53.086Z",
"request": "https://localhost:9443/fhir-server/api/v4/$import",
"requiresAccessToken": false,
"output": [
{
"type": "OperationOutcome",
"url": "AllergyIntolerance.ndjson_oo_success.ndjson",
"count": 1600
}
],
"error": [
{
"type": "OperationOutcome",
"url": "AllergyIntolerance.ndjson_oo_errors.ndjson",
"count": 99
}
]
* Connection #0 to host localhost left intact
}
21. We can check to see if there is data on the server, and there are 1600 resources.
curl -v -k --user 'fhiruser:change-password' 'https://localhost:9443/fhir-server/api/v4/AllergyIntolerance?_format=json'
{"resourceType":"Bundle","id":"d485339f-49e5-4b11-a1be-4ae06edd0b16","type":"searchset","total":1600,"link"...}
22. Now we run $export and capture the content-location:
curl --location --request GET -k -v 'https://localhost:9443/fhir-server/api/v4/$export?_outputFormat=application/fhir+ndjson&_type=AllergyIntolerance' \
--header 'X-FHIR-TENANT-ID: default' \
--header 'Content-Type: application/fhir+json' \
--header 'X-FHIR-BULKDATA-PROVIDER: default' \
--header 'X-FHIR-BULKDATA-PROVIDER-OUTCOME: default' \
--header 'Authorization: Basic ZmhpcnVzZXI6Y2hhbmdlLXBhc3N3b3Jk'
< HTTP/2 202
< content-location: https://localhost:9443/fhir-server/api/v4/$bulkdata-status?job=LqzauvqtHSmkpChVHo%2B1MQ
< date: Wed, 16 Jun 2021 16:39:19 GMT
< content-length: 0
< content-language: en-US
<
* Connection #0 to host localhost left intact
* Closing connection 0
23. Poll and check the response 202 or 200 and the contents of the response. (empty until done)
curl -k --user 'fhiruser:change-password' 'https://localhost:9443/fhir-server/api/v4/$bulkdata-status?job=LqzauvqtHSmkpChVHo%2B1MQ'
{
"transactionTime": "2021-06-16T16:39:26.551Z",
"request": "https://localhost:9443/fhir-server/api/v4/$export?_outputFormat=application/fhir+ndjson&_type=AllergyIntolerance",
"requiresAccessToken": false,
"output": [
{
"type": "AllergyIntolerance",
"url": "c7GRY0uFsu-hO_QlquVZJr_7hTg5c_m6SHlZ1z3Z7J4/AllergyIntolerance_1.ndjson",
"count": 1600
}
]
}
24. Check Azure Blob Service and you’ll see the folder and file on Azure Blob Service

25. Download and check there are 1600 lines.

You now know how to use Azure and Bulk Data.
I’ve been debugging a drop tablespace issue on Db2 – IBM/FHIR: 2354. The core issue was a timing problem with lots of partitions dettaching. This adds a delay to the dropTablespace so the async operation can complete, and cleanly exit with a specific error code so downstream consumers can work around the issue.
When debugging why a partition hasn’t dettached, I found that it’s worth checking the details when a drop tablespace fails:
[db2inst1@53fe3a4d3ad2 ~]$ db2 list utilities show detail
ID = 18435
Type = ASYNCHRONOUS PARTITION DETACH
Database Name = FHIRDB
Member Number = 0
Description = Finalize the detach for partition '3' of table 'FHIRDATA.PARAMETER_NAMES'
Start Time = 06/08/2021 16:31:05.526513
State = Executing
Invocation Type = Automatic
Progress Monitoring:
Description = Performing detach operation and
making the target table available; new compilations blocked
Start Time = 06/08/2021 16:31:10.836936
I’ve attached a useful partition.sql to demo partitions and check the system catalog.
Links
I needed to verify my postgres function.
SELECT pg_get_functiondef(f.oid)
FROM pg_catalog.pg_proc f
INNER JOIN pg_catalog.pg_namespace n ON (f.pronamespace = n.oid)
WHERE n.nspname = 'fhirdata';
SELECT *
FROM pg_catalog.pg_proc f
INNER JOIN pg_catalog.pg_namespace n ON (f.pronamespace = n.oid)
WHERE n.nspname = 'fhirdata';
I wanted to figure out why my code was failing to connect to the backend S3 bucket.
I used the environment variable in my docker image called TRACE_SPEC. TRACE_SPEC is loaded into the logging as the traceSpecification.
I set this to *=info:com.ibm.cloud.*=FINEST which spits out great detail to S3 using the IBM COS SDK.
The output looks like:
With this level of trace, you can really dive into the connection, and determine what is going on.
Note, if you want the whole picture of what is happening with COS and JavaBatch and BulkData, use the following:
*=info:com.ibm.fhir.*=finer:RRA=all:WAS.j2c=all:com.ibm.ws.jdbc.*=all:Transaction=all:ConnLeakLogic=all:Transaction=all:com.ibm.ws.transaction.services.WebAppTransactionCollaboratorImpl=all:RRA=all:com.ibm.cloud.*=FINEST
This was super helpful for debugging S3 files/folders.
brew install rclone
rclone rcd --config rclone.conf --rc-web-gui --no-check-certificate --rc-user rc --rc-pass rc
[minio]
type = s3
env_auth = false
access_key_id = minio
secret_access_key = demooo-password
region = us-east-1
endpoint = https://localhost:9000
location_constraint =
server_side_encryption =
rclone sync --config rclone-s3.conf --no-check-certificate -i fhir-performance:fhir-performance $(pwd)
If you hit this issue, per the GitHub community it’s a Windows issue that can be worked around with a Pagefile. link
[ERROR] OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(0x0000000700000000, 553648128, 0) failed; error='The paging file is too small for this operation to complete' (DOS error/errno=1455)
I used the discussed GitHub Action configure-pagefile
You can see the solution inline at GitHub Actions Workflow
- name: Configure Pagefile
uses: al-cheb/configure-pagefile-action@v1.2
with:
minimum-size: 8GB
maximum-size: 8GB
disk-root: "C:"
Here are some examples I generated from a demo with the IBM FHIR Server
Bundle.entry.resource.ofType(Observation).subject[0].reference.value
Output
Patient/ebb16c62-cb06-4ff8-8ce8-ccb865e7a240
Bundle.entry.resource.ofType(Observation).subject.first().reference.value
Output
Patient/ebb16c62-cb06-4ff8-8ce8-ccb865e7a240
Bundle.entry.resource.ofType(Observation).where(component.where(code.coding.code = '8462-4').value.where( value > 90.0).exists()).subject.reference.value.first()
Output
Patient/5fabfccd-254e-42af-bed2-84199a5c05f2
Bundle.entry.resource.ofType(Patient).where(id = '" + v.split("/")[1] + "').name.given.value
Output
John