Overview
HGT EAM WebServices implements a sophisticated SQLite-based caching system that dramatically improves performance when querying Infor EAM’s SOAP-based grid services. The cache system transforms slow SOAP calls (often 10-30 seconds for large datasets) into sub-second JSON API responses.Performance Impact: First request may take 10-30s while fetching from EAM. Subsequent requests return in under 1 second from SQLite cache.
Why SQLite?
The architecture uses SQLite instead of Redis or in-memory caching for several key reasons:Zero Dependencies
No external cache server required - works out of the box in Docker, Kubernetes, or bare metal.
Persistence
Cache survives application restarts, reducing load on EAM servers during deployments.
Low Memory Footprint
Large datasets (100k+ rows) stay on disk instead of consuming application memory.
Built-in Transactions
ACID compliance ensures cache integrity even during concurrent writes.
Cache Architecture
Database Schema
The cache uses three main tables defined inGridCacheDbContext.cs:9-28:
Table Purposes:
- GridCacheEntry: Metadata about the cached grid (status, record count, creation time)
- GridCacheFieldEntity: Column definitions (names, types, display properties)
- GridCacheRowEntity: Actual row data stored as JSON strings
Cache Key Generation
Cache keys are generated using SHA-256 hashing to uniquely identify each query combination:GridCacheService.cs:126-140
Cache keys include username and organization, so different users/orgs never share cached data even for the same grid.
Caching Workflow
Complete Request Lifecycle
Step-by-Step Process
Cache Key Generation
The system generates a unique SHA-256 hash based on:
- Username and organization
- Grid ID and name
- Filter criteria (date ranges, dataspy ID)
- Function name
Cache Lookup
The handler attempts to retrieve the requested page from cache:The cache validates:
GridDataOnlyGetQueryHandler.cs:39
- Entry exists with
Status = "Completed" - Entry is not expired (default 60 minutes)
- Data integrity is intact
Begin Cache Session (Cold Start)
If cache miss, initialize a new cache session:This creates:
GridCacheService.cs:60-105
GridCacheEntrywithStatus = "Pending"GridCacheFieldEntityrecords for column definitions
Streaming Data Fetch
The
EamGridFetcher retrieves data in batches of 5000 rows:EamGridFetcher.cs:111-191
Complete Cache Session
After all data is fetched, mark the cache as ready:
GridCacheService.cs:107-124
Cache Operations
Reading from Cache
TheGetPageAsync method implements efficient pagination:
GridCacheService.cs:154-236
Writing to Cache
Batch writes use transactions to ensure atomicity:GridCacheService.cs:28-58
Cache Invalidation
The system automatically invalidates cache entries when:- Expiration time reached (default: 60 minutes)
- Incomplete cache detected (Status = “Pending” on read)
- Data integrity failure (row count mismatch)
GridCacheService.cs:309-314
Configuration
Configure caching behavior inappsettings.json:
Enable or disable SQLite caching globally. If
false, all requests hit EAM directly.Minutes until a cache entry expires. Set to
0 for no expiration (not recommended).SQLite database file path. Relative paths are resolved from the application directory.
Performance Benchmarks
First Request (Cache Miss)
10-30 secondsFetches full dataset from EAM SOAP service and populates SQLite cache.
Subsequent Requests (Cache Hit)
< 1 secondRetrieves data directly from SQLite with pagination.
Large Datasets (100k rows)
First fetch: ~45 seconds
Cached reads: ~500ms per page
Cached reads: ~500ms per page
Memory Usage
Without cache: ~500MB for 100k rows
With SQLite cache: ~50MB (90% reduction)
With SQLite cache: ~50MB (90% reduction)
Error Handling
Incomplete Cache Detection
If a cache session fails mid-fetch (e.g., network error), the status remains"Pending":
GridCacheService.cs:170-178
Data Integrity Verification
After fetching, the system verifies row count matches:EamGridFetcher.cs:206-218
Best Practices
Expiration Time
Expiration Time
Choose expiration based on data freshness requirements:
- Financial data: 15-30 minutes
- Operational data: 5-10 minutes
- Historical/read-only data: 24 hours or more
Database Maintenance
Database Maintenance
SQLite performs well but benefits from occasional maintenance:Consider running these during low-traffic periods.
Monitoring Cache Hit Rate
Monitoring Cache Hit Rate
Monitor logs for cache effectiveness:Low hit rates may indicate:
- Expiration time too short
- High query parameter variation
- Users requesting unique data combinations
Troubleshooting
Database Locked Errors
Database Locked Errors
SQLite uses file-level locking. If you see “database is locked” errors:
- Check for concurrent writes from multiple instances
- Ensure
BusyTimeoutis set (currently: 30 seconds) - Consider switching to WAL mode for better concurrency:
Large Database Files
Large Database Files
SQLite databases grow as data accumulates. To manage size:
- Reduce expiration time to purge old data faster
- Run VACUUM periodically to reclaim space
- Implement cleanup job to delete entries older than 7 days:
Slow First Requests
Slow First Requests
Large datasets (50k+ rows) take time to fetch initially. To improve UX:
- Pre-warm cache for common queries during off-peak hours
- Adjust page size - smaller pages return faster on first request
- Use async/await patterns in client applications
- Consider background jobs to populate cache proactively
Next Steps
Architecture Overview
Understand how caching fits into the overall system design
Authentication
Learn about security and rate limiting
API Reference
See how pagination parameters affect cache usage
Setup Guide
Configure caching for production environments