feat(go-services): inventory-go Phase C — ingestion (validated, isolated DB)
Wires the validated item-processor into the ingestion endpoints, writing to an isolated inventory-go-db (never production): - schema.go: faithful 7-table replica of inventory-service/database.py. - ingest.go: /process-inventory (full replace), POST/DELETE single item, with the exact delete-then-insert flow, dynamic INSERT builder (quotes reserved "unique"), spell union (is_active), and item_raw_data verbatim. enhancements always inserts. - compose: isolated inventory-go-db (postgres:14, 127.0.0.1:5435) + read-write inventory-go-shadow (:8773) that owns it; schema init on boot. Validated by ingesting a recently-ingested character's items (from production's original_json) into the shadow DB and diffing vs production: byte-identical — items 243, combat 243, enhancements 243, ratings 6, requirements 19, spells 52 all match; 0 per-column mismatches across 243 items. Finding: older production normalized rows can be STALE (predate the code reading Decal keys 218103832/218103835); Go matches the CURRENT Python code, so validate ingestion against recently-ingested characters. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
b90b52c515
commit
c49b81c237
5 changed files with 466 additions and 3 deletions
127
go-services/inventory-go/schema.go
Normal file
127
go-services/inventory-go/schema.go
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
// initSchema creates the normalized inventory schema on an ingest-owned database
|
||||
// (a faithful replica of inventory-service/database.py). Run only when this
|
||||
// instance owns its DB (READ_ONLY=false) — never against production. Idempotent;
|
||||
// logs and continues per statement.
|
||||
func initSchema(ctx context.Context, pool *pgxpool.Pool, log *slog.Logger) {
|
||||
stmts := []string{
|
||||
`CREATE TABLE IF NOT EXISTS items (
|
||||
id SERIAL PRIMARY KEY,
|
||||
character_name VARCHAR(50) NOT NULL,
|
||||
item_id BIGINT NOT NULL,
|
||||
timestamp TIMESTAMP NOT NULL,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
icon INTEGER NOT NULL,
|
||||
object_class INTEGER NOT NULL,
|
||||
value INTEGER DEFAULT 0,
|
||||
burden INTEGER DEFAULT 0,
|
||||
current_wielded_location INTEGER DEFAULT 0,
|
||||
container_id BIGINT DEFAULT 0,
|
||||
slot INTEGER DEFAULT -1,
|
||||
bonded INTEGER DEFAULT 0,
|
||||
attuned INTEGER DEFAULT 0,
|
||||
"unique" BOOLEAN DEFAULT false,
|
||||
stack_size INTEGER DEFAULT 1,
|
||||
max_stack_size INTEGER DEFAULT 1,
|
||||
items_capacity INTEGER,
|
||||
containers_capacity INTEGER,
|
||||
structure INTEGER,
|
||||
max_structure INTEGER,
|
||||
rare_id INTEGER,
|
||||
lifespan INTEGER,
|
||||
remaining_lifespan INTEGER,
|
||||
has_id_data BOOLEAN DEFAULT false,
|
||||
last_id_time BIGINT DEFAULT 0,
|
||||
CONSTRAINT uq_char_item UNIQUE (character_name, item_id)
|
||||
)`,
|
||||
`CREATE INDEX IF NOT EXISTS ix_items_character_name ON items (character_name)`,
|
||||
`CREATE INDEX IF NOT EXISTS ix_items_name ON items (name)`,
|
||||
`CREATE INDEX IF NOT EXISTS ix_items_object_class ON items (object_class)`,
|
||||
`CREATE INDEX IF NOT EXISTS ix_items_current_wielded_location ON items (current_wielded_location)`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS item_combat_stats (
|
||||
item_id INTEGER PRIMARY KEY REFERENCES items(id),
|
||||
max_damage INTEGER, damage INTEGER, damage_type INTEGER, damage_bonus DOUBLE PRECISION,
|
||||
elemental_damage_bonus INTEGER, elemental_damage_vs_monsters DOUBLE PRECISION, variance DOUBLE PRECISION,
|
||||
cleaving INTEGER, crit_damage_rating INTEGER, damage_over_time INTEGER,
|
||||
attack_bonus DOUBLE PRECISION, weapon_time INTEGER, weapon_skill INTEGER,
|
||||
armor_level INTEGER, shield_value INTEGER, melee_defense_bonus DOUBLE PRECISION,
|
||||
missile_defense_bonus DOUBLE PRECISION, magic_defense_bonus DOUBLE PRECISION,
|
||||
resist_magic INTEGER, crit_resist_rating INTEGER, crit_damage_resist_rating INTEGER,
|
||||
dot_resist_rating INTEGER, life_resist_rating INTEGER, nether_resist_rating INTEGER,
|
||||
heal_over_time INTEGER, healing_resist_rating INTEGER, mana_conversion_bonus DOUBLE PRECISION,
|
||||
pk_damage_rating INTEGER, pk_damage_resist_rating INTEGER, gear_pk_damage_rating INTEGER,
|
||||
gear_pk_damage_resist_rating INTEGER,
|
||||
base_armor_level INTEGER, base_max_damage INTEGER, base_attack_bonus DOUBLE PRECISION,
|
||||
base_melee_defense_bonus DOUBLE PRECISION, base_elemental_damage_vs_monsters DOUBLE PRECISION,
|
||||
base_mana_conversion_bonus DOUBLE PRECISION
|
||||
)`,
|
||||
`CREATE INDEX IF NOT EXISTS ix_combat_armor ON item_combat_stats (armor_level)`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS item_requirements (
|
||||
item_id INTEGER PRIMARY KEY REFERENCES items(id),
|
||||
wield_level INTEGER, wield_requirement INTEGER, skill_level INTEGER,
|
||||
lore_requirement INTEGER, equip_skill VARCHAR(50), mastery VARCHAR(50)
|
||||
)`,
|
||||
`CREATE INDEX IF NOT EXISTS ix_req_level ON item_requirements (wield_level)`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS item_enhancements (
|
||||
item_id INTEGER PRIMARY KEY REFERENCES items(id),
|
||||
material VARCHAR(50), imbue VARCHAR(50), tinks INTEGER, workmanship DOUBLE PRECISION,
|
||||
salvage_workmanship DOUBLE PRECISION, num_times_tinkered INTEGER DEFAULT 0,
|
||||
free_tinkers_bitfield INTEGER, num_items_in_material INTEGER,
|
||||
imbue_attempts INTEGER DEFAULT 0, imbue_successes INTEGER DEFAULT 0,
|
||||
imbued_effect2 INTEGER, imbued_effect3 INTEGER, imbued_effect4 INTEGER, imbued_effect5 INTEGER,
|
||||
imbue_stacking_bits INTEGER, item_set VARCHAR(100), equipment_set_extra INTEGER,
|
||||
aetheria_bitfield INTEGER, heritage_specific_armor INTEGER, shared_cooldown INTEGER
|
||||
)`,
|
||||
`CREATE INDEX IF NOT EXISTS ix_enh_material_set ON item_enhancements (material, item_set)`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS item_ratings (
|
||||
item_id INTEGER PRIMARY KEY REFERENCES items(id),
|
||||
damage_rating INTEGER, damage_resist_rating INTEGER, crit_rating INTEGER,
|
||||
crit_resist_rating INTEGER, crit_damage_rating INTEGER, crit_damage_resist_rating INTEGER,
|
||||
heal_boost_rating INTEGER, vitality_rating INTEGER, healing_rating INTEGER,
|
||||
mana_conversion_rating INTEGER, weakness_rating INTEGER, nether_over_time INTEGER,
|
||||
healing_resist_rating INTEGER, nether_resist_rating INTEGER, dot_resist_rating INTEGER,
|
||||
life_resist_rating INTEGER, sneak_attack_rating INTEGER, recklessness_rating INTEGER,
|
||||
deception_rating INTEGER, pk_damage_rating INTEGER, pk_damage_resist_rating INTEGER,
|
||||
gear_pk_damage_rating INTEGER, gear_pk_damage_resist_rating INTEGER,
|
||||
gear_damage INTEGER, gear_damage_resist INTEGER, gear_crit INTEGER, gear_crit_resist INTEGER,
|
||||
gear_crit_damage INTEGER, gear_crit_damage_resist INTEGER, gear_healing_boost INTEGER,
|
||||
gear_max_health INTEGER, gear_nether_resist INTEGER, gear_life_resist INTEGER,
|
||||
gear_overpower INTEGER, gear_overpower_resist INTEGER, total_rating INTEGER
|
||||
)`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS item_spells (
|
||||
item_id INTEGER REFERENCES items(id),
|
||||
spell_id INTEGER,
|
||||
is_active BOOLEAN DEFAULT false,
|
||||
PRIMARY KEY (item_id, spell_id)
|
||||
)`,
|
||||
|
||||
`CREATE TABLE IF NOT EXISTS item_raw_data (
|
||||
item_id INTEGER PRIMARY KEY REFERENCES items(id),
|
||||
int_values JSONB, double_values JSONB, string_values JSONB, bool_values JSONB,
|
||||
original_json JSONB
|
||||
)`,
|
||||
}
|
||||
ok, failed := 0, 0
|
||||
for _, s := range stmts {
|
||||
if _, err := pool.Exec(ctx, s); err != nil {
|
||||
failed++
|
||||
log.Warn("schema statement failed (continuing)", "err", err)
|
||||
continue
|
||||
}
|
||||
ok++
|
||||
}
|
||||
log.Info("inventory schema init complete", "ok", ok, "failed", failed)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue