Super Speedy Imports — architecture overview
Developer/LLM-oriented map of how SSI is put together: the major components, what each does, the notable design decisions, and where the extension points are. For how to build add-ons and templates, see creating-addons and creating-custom-templates; for the stage system in depth see stage-system-reference and how-imports-work-stage-pipeline.
Table of Contents
Summary — components at a glance
| Component | File(s) | Purpose |
|---|---|---|
| Main plugin | super-speedy-imports.php (class SuperSpeedyImports) |
Admin UI, AJAX/REST handlers, enqueue, DB-version migration, auth toggle. |
| Import orchestrator | run-import.php |
The wp ssi <id> entry. Runs stages sequentially (AJAX) or fans out parallel stages across worker subprocesses (CLI). |
| Template hierarchy | import-template-abstract.php + templates/ |
SSI_ImportTemplate (abstract) → SSI_PostTemplate → SSI_WooCommerceProductTemplate; plus SSI_CustomPostTypeTemplate. Defines field mapping, stage registration, batch-table schema. |
| Stage pipeline | stages/{core,post,woocommerce,utils}/ |
The unit of work. Each stage is a set-based SQL operation over the per-import batch table. |
| DB layer | functions/db.php |
ssi_query / ssi_get_results / ssi_get_var / ssi_insert over a wpdb-or-direct-mysqli switch. |
| Image lookup | functions/image-lookup.php + wp_ssi_image_lookup table |
URL→attachment dedup by url_hash = UNHEX(MD5(url)); replaced original_image_url postmeta in 2.55. |
| Import history / metrics | functions/import-history.php (ImportLogger) |
Per-stage timings + counters, aggregated across workers, into wp_ssi_import_history(_stages). |
| Perf options | functions/perf-options.php |
Global + per-stage worker counts and batch-size knobs (ssi_perf_*). |
| Auth | functions/class-ssi-auth-*.php |
Signed token carrying the stage plan + version constraints; soft-warn or hard-block. |
| Tools | tools/class-tools.php + tools/* |
Standalone utilities. Some are id-prefixed (wp ssi <id> save-posts / sanitize-terms / list-files); others take NO import id (wp ssi export …, wp ssi resync-image-lookup). |
| ACF/SCF | functions/acf-*.php |
Custom-field group/repeater expansion into postmeta. |
Unusual / interesting aspects (expanded at the end):
– Set-based, not per-row. Stages are bulk SQL over a staging batch table, not a per-product loop — this is the core reason SSI is fast at scale.
– Per-import disposable batch tables, intentionally left on disk after a run for post-mortem.
– Parallel stages via proc_open worker subprocesses, each owning a segment range; coordination is purely through the shared batch table (no IPC).
– wpdb-or-direct-mysqli switch to dodge per-connection memory overhead and WP filter chains when needed.
– Image dedup table (wp_ssi_image_lookup) with a BINARY(16) URL hash — constant-time lookups regardless of URL length, and INSERT IGNORE makes concurrent workers race-safe.
– Recursive-CTE hierarchy resolution done once in a stage finalizer, not per row.
– Signed stage plan — stage names/priorities come from an auth token, so the pipeline is server-defined.
Request lifecycle
Admin UI (AJAX) ─┐
├─► ssi_run_import($args,$assoc) [run-import.php]
wp ssi <id> ─────┘ │
├─ AJAX/REST → $template->runStages() (sequential, in-process)
├─ CLI → ssi_run_parallel() (fan-out orchestrator)
└─ <tool> → $template->runUtility()/tools
ssi_run_import builds the template instance (resolving base_template via ssi_resolve_template_class()), parses flags (--workers, --file, --show-sql, --delete-all, --verbose), applies the import-mode preset, and dispatches.
Notable components
Orchestrator (run-import.php)
ssi_run_parallel($template,$id,$workers)iterates the stage list fromgetStagesWithParallelInfo(). For each stage:setupStageAuxTables()(DROP+CREATE any stage-specific aux tables) runs first — in all execution modes.- If the stage is not parallel → run in-process.
- If parallel and
effective_workers === 1→ single-worker bypass: run in-process (noproc_open) but still callfinalizeStage(). - If parallel and
> 1→ build one worker command per segment (ssi_build_worker_command) and spawn viassi_spawn_workers(proc_open); thenfinalizeStage(). - Segment sources:
csv(byte-offset row ranges, fromcountCsvRows),batch/terms/hierarchies(ID ranges from captured max-IDs). finalizeStage($name)is the reduce step — runs once on the parent after workers finish (e.g.load-csv: dedupe UIDs, build post-load indexes, capture max-IDs;import-taxonomies: recursive-CTE term-taxonomy propagation). Called in every mode, so single- and multi-worker runs produce identical results.- Parallel stages (
$parallel_stagesinimport-template-abstract.php):load-csv(csv),attach-existing-images(batch),upload-remote-images(batch),fix-attributes(batch). Everything else is sequential.
Template hierarchy (import-template-abstract.php, templates/)
- Abstract
SSI_ImportTemplate; concreteSSI_PostTemplate,SSI_WooCommerceProductTemplate(extends PostTemplate),SSI_CustomPostTypeTemplate. (TheSSI_prefix was added in a rename;functions/template-resolver.php::ssi_resolve_template_class()rewrites legacy unprefixedbase_templatestrings at read time so old saved imports still instantiate.) - Templates register stages (
registerStage($name,$priority,$callback,$desc)), define the batch-table schema (setupBatchTables), the field-mapping UI shape, andgetStagePresets()(import modes:full,only_insert,only_update,prices_only,taxonomies_only,images_only). - Asset auto-discovery: a template’s
{ClassName}.js/.cssare auto-enqueued (theSSI_prefix is stripped when deriving the asset filename).
Stage pipeline
Core (stages/core/): load-csv → import-taxonomies → match-existing → update-posts → insert-posts → update-postmeta → insert-postmeta → upsert-relationships. Post (stages/post/): attach-existing-images, upload-remote-images. WooCommerce (stages/woocommerce/): attach-gallery-images, fix-attributes, variable-products, synthesise-parent-products. Utils (stages/utils/): save-posts, sanitize-terms, list-files. Each stage is set-based SQL over the batch table; full per-stage detail in stage-system-reference.
Database layer
- Per-import tables
wp_super_speedy_imports_<type>_<id>:batch(the staging table; UNIQUE on_ssi_unique_item_id),terms,hierarchies(generatedparent_keycolumn collapses NULL→0 for cross-worker UNIQUE dedup),gallery_images,deletes,ambiguous_terms/_hierarchies,duplicate_unique_ids,validation_errors,acf_expanded. Left on disk after a run for diagnostics; recreated next run. functions/db.phpwraps writes/reads with assi_should_use_wpdb()switch — wpdb by default, direct mysqli when memory pressure or--show-sqlmode makes wpdb’s per-connection overhead / filter chains undesirable. Add-ons/stages should callssi_insert/ssi_query/ssi_get_results, never$wpdbdirectly.- History:
wp_ssi_import_history+_stagesviaImportLogger; metrics are numeric and summed across workers (outputAggregatedStageSummary).
Image pipeline
SSI_PostTemplate::uploadImage()downloads, validates MIME, andmedia_handle_sideload()s insidewithImageSizesFilter()(which can suppress intermediate-size generation), then registers the result viassi_image_lookup_insert().wp_ssi_image_lookup(global) mapsurl_hash(BINARY(16) MD5) →attachment_id. Lookups are O(1)-ish regardless of URL length;INSERT IGNOREon the uniqueurl_hashmakes parallel workers race-safe.ssi_image_lookup_cleanup_stale()(anti-join) prunes rows for attachments deleted in wp-admin, run at the top of image stages. This replaced the pre-2.55original_image_urlpostmeta contract — never read/write that postmeta; usefunctions/image-lookup.php.
Taxonomy handling
load-csv parses each taxonomy column into hierarchy levels (the configured separator, e.g. >) and multiple terms (the | multi-term separator). Flat terms resolve to term_taxonomy_id during import-taxonomies; hierarchical paths are staged and resolved by a recursive-CTE bulk UPDATE in finalizeStage('import-taxonomies'). Slug collisions (two source values sanitising to the same slug) follow term_slug_collision_policy (alert vs merge); genuinely ambiguous paths surface in the ambiguous_* tables.
Hook points (brief)
Add-ons/templates extend SSI through actions/filters — full how-to in creating-addons. The notable ones:
| Hook | Type | When / use |
|---|---|---|
ssi_register_templates |
action | Register external template classes (SSI_ImportTemplate::addTemplate([...])). |
ssi_register_stages / registerStage() |
action / method | Add custom stages to a template instance. |
ssi_before_import_stages / ssi_after_import_stages |
action | Bracket the whole import. |
ssi_filter_stages_to_run |
filter | Add/remove stages (import-mode preset runs here at priority 5; CLI explicit-stage at 10). |
ssi_stage_starting / ssi_stage_ending |
action | Per-stage brackets (ACF postmeta expansion hangs off ssi_stage_ending). |
ssi_after_load_csv_synthesise |
action | Post-load row synthesis (variable-product parents). |
ssi_apply_additional_options |
action | Mutate template.config from additional_options (e.g. the Delayed-Image-Import add-on). |
ssi_session_tuning_for_stage |
filter | Return SET SESSION tuning for a stage (used by upsert-relationships). |
ssi_template_js_files / ssi_template_css_files |
filter | Register external-template assets. |
ssi_allow_private_image_hosts / ssi_allowed_downloaded_image_mimes / https_local_ssl_verify |
filter | Image-download SSRF gate, MIME allow-list, SSL verify. |
ssi_filter_deletes |
filter | Constrain the delete-items WHERE clause (e.g. keep sold items). |
ssi_standalone_tools |
filter | Register custom wp ssi <id> <tool> utilities. |
Add-ons detect SSI by checking class_exists('SSI_ImportTemplate') (note the SSI_ prefix).
Why it’s built this way (interesting aspects)
- Bulk SQL over a batch table is the central idea: load the CSV into a staging table once, then every stage is a small number of set-based statements (
INSERT … SELECT,UPDATE … JOIN) instead of N per-row WordPress calls. This is what makes it scale (no-op updates short-circuit; unchanged rows cost ~nothing). - Parallelism is opt-in per stage and segment-based: the orchestrator splits the work into ID/byte ranges and runs
wp ssiworker subprocesses; they never talk to each other, only to the shared batch table. The single-worker bypass keeps the same code path correct without spawning. - Disposable per-import tables trade a little disk for debuggability — after a run you can inspect exactly what each stage saw.
- The image-lookup table is a deliberate move away from
wp_postmetalookups: a dedicated, well-indexed, collation-controlled table the plugin owns, so URL→attachment resolution stays fast and side-effect-free at scale. - Hierarchy resolution as one recursive CTE in a finalizer avoids per-row term queries and is computed once per import regardless of worker count.
- The stage plan is signed (auth token), so the set and ordering of stages is defined server-side and validated by every worker — preventing version skew between the orchestrator and its workers.
Maintenance note: avoid citing line numbers in this doc — they drift. Names of classes, functions, hooks, tables and stages are the stable references.