Super Speedy Imports — architecture overview

June 5, 2026

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.


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_PostTemplateSSI_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 from getStagesWithParallelInfo(). 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 === 1single-worker bypass: run in-process (no proc_open) but still call finalizeStage().
  • If parallel and > 1 → build one worker command per segment (ssi_build_worker_command) and spawn via ssi_spawn_workers (proc_open); then finalizeStage().
  • Segment sources: csv (byte-offset row ranges, from countCsvRows), 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_stages in import-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; concrete SSI_PostTemplate, SSI_WooCommerceProductTemplate (extends PostTemplate), SSI_CustomPostTypeTemplate. (The SSI_ prefix was added in a rename; functions/template-resolver.php::ssi_resolve_template_class() rewrites legacy unprefixed base_template strings 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, and getStagePresets() (import modes: full, only_insert, only_update, prices_only, taxonomies_only, images_only).
  • Asset auto-discovery: a template’s {ClassName}.js/.css are auto-enqueued (the SSI_ prefix is stripped when deriving the asset filename).

Stage pipeline

Core (stages/core/): load-csvimport-taxonomiesmatch-existingupdate-postsinsert-postsupdate-postmetainsert-postmetaupsert-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 (generated parent_key column 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.php wraps writes/reads with a ssi_should_use_wpdb() switch — wpdb by default, direct mysqli when memory pressure or --show-sql mode makes wpdb’s per-connection overhead / filter chains undesirable. Add-ons/stages should call ssi_insert/ssi_query/ssi_get_results, never $wpdb directly.
  • History: wp_ssi_import_history + _stages via ImportLogger; metrics are numeric and summed across workers (outputAggregatedStageSummary).

Image pipeline

  • SSI_PostTemplate::uploadImage() downloads, validates MIME, and media_handle_sideload()s inside withImageSizesFilter() (which can suppress intermediate-size generation), then registers the result via ssi_image_lookup_insert().
  • wp_ssi_image_lookup (global) maps url_hash (BINARY(16) MD5) → attachment_id. Lookups are O(1)-ish regardless of URL length; INSERT IGNORE on the unique url_hash makes 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.55 original_image_url postmeta contract — never read/write that postmeta; use functions/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 ssi worker 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_postmeta lookups: 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.

×
1/1