Insufficient Integer Size for Growth
Description
Using 32-bit integers for auto-incrementing primary keys or counters that will exceed 2.14 billion, causing complete write failures when the limit is reached. This weakness stems from underestimating data scale or using database defaults without consideration. Modern applications can reach billions of records faster than anticipated, especially with IoT devices, user-generated content, or audit logs.
Illustrative Cantor Point
The Cantor Point occurs during initial table creation when choosing INT over BIGINT. The developer thinks "2 billion is practically infinite" without calculating actual growth rates or considering the system's intended lifespan. This 4-byte savings becomes a time bomb that threatens the entire system's ability to create new records.
Real-World Examples / Observed In
- Heroku (2023): Integer overflow in authorization table caused platform-wide outage, preventing all deployments when the ID exceeded 32-bit limits
- Etsy: Preemptively shut down the site when discovering an ID approaching 32-bit limit to prevent data corruption
- Common Pattern: Many startups hit this limit 3-5 years after launch when initial "we'll never have that many records" assumptions prove false
Common Consequences & Impacts
Technical Impacts
- - Complete write failures on affected tables
- - Foreign key constraint violations in related tables
- - Emergency migrations requiring table locks
- - Potential data corruption if overflow isn't caught
Human/Ethical Impacts
- - Users unable to access services
- - Developers working through nights on emergency fixes
- - Business operations halted
- - Potential data loss affecting user history
Business Impacts
- - Total service outage (no new users, orders, posts)
- - Emergency maintenance windows
- - Customer trust erosion
- - Revenue loss during downtime
Recovery Difficulty & Escalation
ADI Principles & Axioms Violated
- Principle of Fragile Stability: The system works perfectly until the exact moment it catastrophically fails
- Principle of Invisible Decay: The approaching limit is invisible until it's too late
- Principle of Emergent Transformation: Growth patterns change as systems scale
Detection / 60-Second Audit
-- PostgreSQL: Check INT columns approaching overflow
WITH int_columns AS (
SELECT
schemaname,
tablename,
attname as column_name,
atttypid
FROM pg_stats
WHERE schemaname = 'public'
AND (atttypid = 'int4'::regtype OR atttypid = 'int2'::regtype)
)
SELECT
ic.*,
'SELECT MAX(' || ic.column_name || ') FROM ' || ic.tablename || ';' as check_query,
CASE
WHEN ic.atttypid = 'int4'::regtype THEN '2,147,483,647'
WHEN ic.atttypid = 'int2'::regtype THEN '32,767'
END as max_value
FROM int_columns ic
JOIN pg_stats ps ON ic.tablename = ps.tablename AND ic.column_name = ps.attname
WHERE ps.n_distinct > 1000000 -- Focus on high-cardinality columns
OR ic.column_name LIKE '%_id'
ORDER BY ps.n_distinct DESC;
-- Check actual usage percentage
SELECT
nspname || '.' || relname as table_name,
attname as column_name,
CASE
WHEN atttypid = 'int4'::regtype THEN
ROUND((pg_sequence_last_value(pg_get_serial_sequence(nspname||'.'||relname, attname))::numeric / 2147483647) * 100, 2)
WHEN atttypid = 'int2'::regtype THEN
ROUND((pg_sequence_last_value(pg_get_serial_sequence(nspname||'.'||relname, attname))::numeric / 32767) * 100, 2)
END as percent_used
FROM pg_attribute a
JOIN pg_class c ON a.attrelid = c.oid
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE a.attnum > 0
AND NOT a.attisdropped
AND (a.atttypid = 'int4'::regtype OR a.atttypid = 'int2'::regtype)
AND pg_get_serial_sequence(n.nspname||'.'||c.relname, a.attname) IS NOT NULL
ORDER BY percent_used DESC NULLS LAST;
-- MySQL: Find INT columns that may overflow
SELECT
TABLE_NAME,
COLUMN_NAME,
DATA_TYPE,
COLUMN_TYPE,
CASE
WHEN DATA_TYPE = 'int' AND COLUMN_TYPE LIKE '%unsigned%' THEN '4,294,967,295'
WHEN DATA_TYPE = 'int' THEN '2,147,483,647'
WHEN DATA_TYPE = 'mediumint' AND COLUMN_TYPE LIKE '%unsigned%' THEN '16,777,215'
WHEN DATA_TYPE = 'mediumint' THEN '8,388,607'
WHEN DATA_TYPE = 'smallint' AND COLUMN_TYPE LIKE '%unsigned%' THEN '65,535'
WHEN DATA_TYPE = 'smallint' THEN '32,767'
END as max_value,
CONCAT('SELECT MAX(', COLUMN_NAME, ') FROM ', TABLE_NAME) as check_query
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND DATA_TYPE IN ('int', 'smallint', 'mediumint')
AND (COLUMN_KEY = 'PRI' OR COLUMN_NAME LIKE '%_id' OR EXTRA LIKE '%auto_increment%');
-- Check AUTO_INCREMENT usage
SELECT
TABLE_NAME,
AUTO_INCREMENT as current_value,
CASE
WHEN DATA_TYPE = 'int' AND COLUMN_TYPE LIKE '%unsigned%' THEN
ROUND((AUTO_INCREMENT / 4294967295) * 100, 2)
WHEN DATA_TYPE = 'int' THEN
ROUND((AUTO_INCREMENT / 2147483647) * 100, 2)
END as percent_used
FROM INFORMATION_SCHEMA.TABLES t
JOIN INFORMATION_SCHEMA.COLUMNS c ON t.TABLE_NAME = c.TABLE_NAME
WHERE t.TABLE_SCHEMA = DATABASE()
AND t.AUTO_INCREMENT IS NOT NULL
AND c.EXTRA LIKE '%auto_increment%'
ORDER BY percent_used DESC;
-- SQL Server: Check for integer columns near limits
SELECT
SCHEMA_NAME(t.schema_id) + '.' + t.name as table_name,
c.name as column_name,
ty.name as data_type,
CASE
WHEN ty.name = 'int' THEN '2,147,483,647'
WHEN ty.name = 'smallint' THEN '32,767'
WHEN ty.name = 'tinyint' THEN '255'
END as max_value,
'SELECT MAX(' + c.name + ') FROM ' + SCHEMA_NAME(t.schema_id) + '.' + t.name as check_query
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
JOIN sys.types ty ON c.user_type_id = ty.user_type_id
WHERE ty.name IN ('int', 'smallint', 'tinyint')
AND (c.is_identity = 1 OR c.name LIKE '%_id' OR c.name LIKE '%ID')
ORDER BY t.name, c.name;
-- Check identity column usage
SELECT
SCHEMA_NAME(t.schema_id) + '.' + t.name as table_name,
c.name as column_name,
IDENT_CURRENT(SCHEMA_NAME(t.schema_id) + '.' + t.name) as current_value,
CASE
WHEN ty.name = 'int' THEN
CAST(IDENT_CURRENT(SCHEMA_NAME(t.schema_id) + '.' + t.name) / 2147483647.0 * 100 AS DECIMAL(5,2))
WHEN ty.name = 'smallint' THEN
CAST(IDENT_CURRENT(SCHEMA_NAME(t.schema_id) + '.' + t.name) / 32767.0 * 100 AS DECIMAL(5,2))
END as percent_used
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
JOIN sys.types ty ON c.user_type_id = ty.user_type_id
WHERE c.is_identity = 1
AND ty.name IN ('int', 'smallint')
ORDER BY percent_used DESC;
Prevention & Mitigation Best Practices
- Default to BIGINT: The 4-byte savings is negligible compared to the risk
- Consistent Types: Ensure foreign keys match primary key types exactly
- Growth Projection: Calculate worst-case scenarios - if ANY chance of exceeding 2B, use BIGINT
- Monitoring Thresholds: Alert at 50% capacity (1 billion for INT) to allow migration time
- UUID Alternative: Consider UUIDs for distributed systems or where sequential IDs pose security risks
- Migration Strategy: Plan for online schema changes before they're needed
Real World Examples
-- Heroku authorization table hit INT limit
CREATE TABLE authorizations (
id INT PRIMARY KEY AUTO_INCREMENT, -- 32-bit signed: max 2,147,483,647
app_id INT NOT NULL,
user_id INT NOT NULL,
created_at TIMESTAMP
);
-- After 9 years, ID approached 2.1 billion
-- Platform-wide outage: no new deployments possible
-- IoT sensor data overwhelms counter
CREATE TABLE sensor_readings (
reading_id INT AUTO_INCREMENT PRIMARY KEY,
device_id VARCHAR(50),
temperature DECIMAL(5,2),
recorded_at TIMESTAMP
);
-- 10,000 devices × 1 reading/minute = 14.4M/day
-- Hits INT limit in just 149 days!
-- Always use BIGINT for auto-incrementing IDs
CREATE TABLE authorizations (
id BIGINT PRIMARY KEY AUTO_INCREMENT, -- 64-bit: 9,223,372,036,854,775,807
app_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Would take 292 billion years at 1M inserts/second
-- Monitor approaching limits
SELECT
TABLE_NAME,
AUTO_INCREMENT,
CASE DATA_TYPE
WHEN 'int' THEN 2147483647
WHEN 'bigint' THEN 9223372036854775807
END as max_value,
(AUTO_INCREMENT / CASE DATA_TYPE
WHEN 'int' THEN 2147483647
WHEN 'bigint' THEN 9223372036854775807
END) * 100 as percent_used
FROM information_schema.TABLES t
JOIN information_schema.COLUMNS c
ON t.TABLE_NAME = c.TABLE_NAME
AND t.TABLE_SCHEMA = c.TABLE_SCHEMA
WHERE t.TABLE_SCHEMA = DATABASE()
AND c.COLUMN_KEY = 'PRI'
AND c.EXTRA = 'auto_increment'
AND AUTO_INCREMENT > 1000000;
AI Coding Guidance/Prompt
Prompt: "When creating database tables with ID columns:"
Rules:
- Always use BIGINT for auto-incrementing IDs unless explicitly justified
- Calculate maximum rows over 10-year period before choosing INT
- Flag any INT primary keys in code review
Example:
# Bad: Using INT by default
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT
);
# Good: Future-proof with BIGINT
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT
);
# Also Good: UUID for distributed systems
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid()
);
Relevant Keywords
insufficient integer size growth Symptoms: slow queries, data inconsistency, constraint violations Preventive: schema validation, constraint enforcement, proper typing Tech stack: PostgreSQL, MySQL, SQL Server, Oracle Industry: all industries, enterprise, SaaS