Price Averaging
/ah average shows players the weighted unit price of an item based on real recent sales. The service that powers it runs continuously in the background so the menu lookup is a fast cache hit, not a SQL round-trip.
Pipeline
The SQL
Filter | Why |
|---|---|
| Items without a stable key (e.g. vanilla materials with unique NBT) aren't averaged. |
| Defensive — every legitimate transaction stamps a positive amount. |
| Rolling 90-day window so prices reflect current market. |
| Avoids publishing a "market price" off a single rogue transaction. |
The query is run by TransactionService.fetchAllAverages() and is the authoritative source. Both the periodic refresh (/ah refresh) and the startup warmup go through it.
Item keys
The aggregation groups by the item_key column. Keys are produced by AHUtils.itemKey(stack) at listing time:
Stack | Key |
|---|---|
Vanilla material with no NBT |
|
Mob spawner | entity type — |
Enchanted book |
|
AstralItems / AstralPets / AstralScrolls custom item | The supplier's key (which already encodes rarity, etc.) |
Anything else with PersistentData / damage |
|
Enchantment codes come from a fixed table in AHUtils (e.g. sharpness → sh, protection → pr, unbreaking → un). The list is stable, sorted, and joined by _ — so a book with Sharpness II + Looting III always ends up as the same key regardless of inventory order.
Redis hash
auctionhouse:average is a single Redis hash:
Field | Value |
|---|---|
| weighted unit price as a string |
The hash is written from AverageService.refetch() and read by the Caffeine cache when an entry is missed locally. Storing as a single hash keeps the network footprint low and lets you inspect or hand- patch averages from a Redis CLI without touching Postgres.
Caffeine cache
AverageService keeps an AsyncLoadingCache<String, Integer>:
Refresh after write: 1 hour. Reads return the stale value; the loader runs in the background.
Loader timeout: 5 seconds. If Redis is down, the call resolves to "no data".
Warm-up: the cache is fully populated from Redis on
onEnable.
That gives players instant /ah average responses while the background pipeline keeps the cache fresh without ever spiking the database.
Manual refresh
Forces AverageService.refetch() to run synchronously: the SQL aggregate executes, the Redis hash is overwritten, and the Caffeine cache is invalidated. Use it after a manual DB edit or any time you suspect a stale value (e.g. the 30-minute scheduler missed a tick due to a restart).
Placeholders
For items rendered in menus the same data is available without any extra wiring:
Placeholder | Value |
|---|---|
| Weighted unit price, or |
|
|
See Developer API for the rest of the placeholder catalogue.