Mike's Notes
The Render Engine (rnd) generates code (CFML, JAVA, Python, etc.), HTML, CSS, XML, JSON, YAML, SQL, PDFs, etc. Many of these autonomous engines can run simultaneously, and they need to run much faster.
The Render Engine (rnd) is currently being rebuilt. A base-36 generator is needed to create sequential 10-character strings using 0-9 and a-z.
Eg:
These strings will be used in an automated batch process to create uniquely named "temp" directories to hold rendered output, directory, and file structures before final assembly and deployment. This setup can support 36 quadrillion of these temp directories. 😎
If needed, future major editions of Pipi, like "Pipi 15", could easily replace this generator with one that supports an even larger range. The other option is to recycle a pool of these strings, since they get deleted after use.
I used Google Search AI mode to generate some sample code and find some reading references. I asked lots of dumb questions and gave what-if prompts to explore options. It only took a few minutes to sort out a solution. And no token bill.
Below is an edited transcript, where I deleted all the dead ends to arrive at a workable, 100% reliable solution. The code is designed for PostgreSQL and CFML running on BoxLang on JDK 21.
It still needs testing. Remember, "Use code with caution." 😎😎😎😎
Resources
- https://www.bennadel.com/blog/1540-converting-numbers-to-a-character-set-based-radix-using-coldfusion.htm
- https://www.blog.ajabbi.com/2026/04/agents.html
References
- Reference
Repository
- Home > Ajabbi Research > Library >
- Home > Handbook >
Last Updated
02/11/2026
Render Engine base-36 generator
Mike Peters: Mike invented and designed Pipi and founded Ajabbi.
Google Search AI Mode: Free and works very well.
...
A base-36 10-character alphanumeric sequence (0-9, a-z) has over 3.6 quadrillion (36¹⁰) combinations
...
To generate base-36 alphanumeric sequences (0-9, a-z) natively in PostgreSQL, you can use PostgreSQL's procedural system. The math is more reliable and infinitely customizable if you first create a lightweight helper function in PostgreSQL, then execute it via a single <cfquery> block.
Here is how to set up and run the high-performance database-native approach:
Step 1: Run this once in PostgreSQL
Execute this script inside your database manager (like pgAdmin or DBeaver) to create a custom base-36 conversion function:
CREATE OR REPLACE FUNCTION to_base36(digits bigint, min_width int DEFAULT 10)
RETURNS text AS $$
DECLARE
-- The complete alphanumeric sequence dictionary
chars char[] := ARRAY['0','1','2','3','4','5','6','7','8','9',
'a','b','c','d','e','f','g','h','i','j',
'k','l','m','n','o','p','q','r','s','t',
'u','v','w','x','y','z'];
ret text := '';
val bigint := digits;
BEGIN
-- Loop to convert decimal base-10 to base-36
WHILE val > 0 LOOP
ret := chars[(val % 36) + 1] || ret;
val := val / 36;
END LOOP;
-- If the value was 0, it won't enter the loop
IF ret = '' THEN ret := '0'; END IF;
-- Dynamically left-pad with zeros to match our required 10-char width
RETURN lpad(ret, min_width, '0');
END;
$$ LANGUAGE plpgsql IMMUTABLE;
Step 2: Your CFML Template Code
Once that helper function exists, your tag-based CFML file needs only one query block to populate thousands or millions of sequential records instantly:
<!--- Configuration --->
<cfset variables.datasource = "your_datasource_name">
<cfset variables.startValue = 0>
<cfset variables.endValue = 99999> <!--- This will instantly generate 100,000 unique records --->
<cfsetting requesttimeout="600">
<!--- Single high-performance multi-row native insert --->
<cfquery datasource="#variables.datasource#">
INSERT INTO your_table_name (your_column_name)
SELECT to_base36(val, 10)
FROM generate_series(
<cfqueryparam value="#variables.startValue#" cfsqltype="CF_SQL_BIGINT">,
<cfqueryparam value="#variables.endValue#" cfsqltype="CF_SQL_BIGINT">
) AS val
</cfquery>
<cfoutput>
<p>Success! Generation complete across the database layer.</p>
</cfoutput>
Why this is the best option for PostgreSQL:
- Blazing Fast: Generating 100,000 rows takes less than a second because generate_series() evaluates entirely within the database engine's cache memory.
- Network efficiency: CFML passes exactly 2 integer values to the server (startValue and endValue). The database does all the structural formatting, heavy lifting, and inserts internally.
- No Out-of-Memory Crashes: Traditional server-side loops building massive string payloads can crash your ColdFusion/Lucee JVM heap space if the array size gets too high. This setup bypasses CFML's memory limits entirely.
To meet your requirement of using each row once in sequence to create a directory and track its state, the best practice is to design a table that acts as a pre-generated "pool" of unique strings.
Here is the blueprint to create the table, pre-populate it, and safely fetch the next sequential directory name without race conditions.
Step 1: Create the Database Table
Run this script in your PostgreSQL database. It creates a table with an auto-incrementing serial index, the 10-character Base-36 string, and a boolean flag (is_used) to track whether it has been turned into a directory.
CREATE TABLE directory_pool (
id bigserial PRIMARY KEY,
dir_name varchar(10) NOT NULL UNIQUE,
is_used boolean DEFAULT false NOT NULL
);
-- Index the is_used and id columns for lightning-fast lookups
CREATE INDEX idx_pool_unused ON directory_pool (id) WHERE is_used = false;
Step 2: Pre-populate the Table Instantly
Using the to_base36 function we created earlier, you can populate your first batch of strings. For example, let's generate the first 100,000 sequential names (from 0000000000 to 0000002882 in Base-36) instantly:
<cfset variables.datasource = "your_datasource_name">
<cfset variables.startValue = 0>
<cfset variables.endValue = 99999>
<cfquery datasource="#variables.datasource#">
INSERT INTO directory_pool (dir_name)
SELECT to_base36(val, 10)
FROM generate_series(
<cfqueryparam value="#variables.startValue#" cfsqltype="CF_SQL_BIGINT">,
<cfqueryparam value="#variables.endValue#" cfsqltype="CF_SQL_BIGINT">
) AS val;
</cfquery>
Step 3: Fetch and Lock the Next Row Atomically
If multiple web requests try to create a directory at the exact same millisecond, they might grab the same name. To prevent this, use PostgreSQL's UPDATE ... RETURNING clause. This updates the flag and hands your CFML code the string in a single atomic action, blocking duplicate usage.
Why this design works perfectly:
- Strict Sequence Execution: Sorting by id ASC guarantees you will strictly consume them in sequence (0000000000, 0000000001, 0000000002, etc.).
- Concurrency Safe: Using FOR UPDATE SKIP LOCKED makes this system enterprise-safe. Hundreds of users can trigger directory generation simultaneously, and nobody will ever get the same directory name or lock up the database.
- Audit Trail: You can easily run a query to check how many names are remaining (SELECT count(*) FROM directory_pool WHERE is_used = false;).
No comments:
Post a Comment