Skip to content

Background Jobs Inventory

57 job_*.js files at the repo root. Each runs on a node-cron schedule via sync-mysql.

Pattern

Every file follows the same skeleton:

var conf = require("./conf");
var cron = require("node-cron");
const mysql = require("sync-mysql");
const con = new mysql({ host: conf.host, user: conf.user, password: conf.password, database: conf.database, port: conf.dbPort });
let isOnProcess = false;

cron.schedule("*/N * * * *", () => { doWork(); });

async function doWork() {
  if (!isOnProcess) {
    isOnProcess = true;
    try { /* sync-mysql queries + work */ } catch (e) { /* console.log */ }
    isOnProcess = false;
  }
}

The isOnProcess flag is the only concurrency guard. There is no distributed lock — if the same job ran on multiple replicas, both would think isOnProcess === false and run simultaneously. Currently the deploy is single-instance, so this works; if scaled out, the guard fails.

Categorisation

Industry-specific content generators (~30 files)

These each target one industry vertical's content library. They SELECT from tIndustries WHERE id = <industry-id>, iterate categories under that industry, generate posts.

File Industry id (from code) Cron
job_auto_garge_ceramic_coating.js 289 */5 * * * *
job_auto_garge_painting_body_repairs.js (similar) (similar)
job_auto_garge_window_tinting.js (similar) (similar)
job_building_culture.js ? ?
job_business_networking_referral_marketing.js ? ?
job_change_management.js ? ?
job_commodity_abbitrage_funds.js ? ?
job_common_life_insurance.js ? ?
job_conflict_management.js ? ?
job_cruises_travel.js ? ?
job_cyber_insurance.js ? ?
job_disability_insurance.js ? ?
job_emotional_intelligence.js ? ?
job_entrepreneurship_startups.js ? ?
job_financial_planning_goals.js ? ?
job_hr_complaint_policies.js ? ?
job_hr_review_governance.js ? ?
job_hr_talent_mapping.js ? ?
job_human_resources.js ? ?
job_interior_fitout.js ? ?
job_investing.js ? ?
job_It_cyber_security.js ? ?
job_marketing.js ? ?
job_motor_insurance.js ? ?
job_pet_food.js ? ?
job_recruitment.js ? ?
job_recruitment_selection.js ? ?
job_stroke_insurance.js ? ?
job_travel.js ? ?
job_travel_insurance.js ? ?
job_uae_estate.js ? ?
job_uae_labour_law.js ? ?
job_us_mortgage_loans.js ? ?
job_valor_insurance.js ? ?
job_warehouse_management.js ? ?

Observation: each file is a near-clone of every other, parametrised by industry id. This is a massive maintenance liability — fixing a bug requires editing 30+ files. A proper refactor would replace these 30+ files with a single job_industry_content_generator.js that loops over a configured list of industry ids. See enterprise-readiness.md Phase 1.

Validators / checkers (~6 files)

File Purpose
job_check_color_with_json.js Validate that media's color matches its JSON descriptor; every 5 min
job_check_temp_color_with_json.js Same for templates
job_check_media_json.js JSON-vs-media validation
job_check_mediaTemplates_json.js JSON-vs-template validation
job_check_temp_valid.js Template validity
job_createdByName_Temp.js Backfill createdByName field

Schedulers (~5 files)

File Purpose
job_schedule_dynamic.js Generic dynamic post schedule (every hour)
job_schedule_organization.js Per-organization schedule
job_schedule_template.js Template-driven schedule
job_schedule_variant.js Variant-driven schedule
job_16_30_schedule_template.js Specific 16:30 schedule (hardcoded time)

Approval / workflow

File Purpose
job_post_approved.js Post-approval workflow (every 1 min)
job_preprodpost_approved.js Same for pre-production posts
job_specific_approved_post.js Specific approved post

Content swap / utility

File Purpose
job_content_swap_entry.js content swap entry
job_swap.js content swap
job_id_card.js id card generation
job_glass_design.js glass design
job_gemini_Images.js Gemini image generation
job_get_image.js image retrieval
job_update_get_image.js update + retrieve
job_tAutoDesignPost_delete_cron.js cleanup deletion (every 1 day)

No overlap with someli-api job filenames

Zero filename overlap with the 108 job_*.js files in someli-api. The patterns and purposes are also distinct:

Repo Job scope Examples
someli-api/job_* Runtime social-media pipeline publishing, token refresh, paddle webhooks, post validation
designer-api/job_* Template / content factory industry generators, validators, schedulers

This split is the functional reason designer-api exists separately — it is the "content factory" worker pool feeding the same database that someli-api reads from.

PM2 management

There is no ecosystem.config.js in this repo, unlike someli-api. The 57 jobs are presumably either:

  • Each pm2 start'd manually on the deploy box, or
  • Imported into server.js and run inside the same Node process (verify by grep -E "require\(.*job_" server.js routes/routes.js)
  • Or some hybrid

Action: add a PM2 ecosystem manifest. The deploy story today depends on operator memory.

Connection pool footprint

57 jobs × 1 sync-mysql connection per job = 57 idle DB connections, plus the server.js's pool (callback driver + mysql2/promise pool, ~10-20 connections), plus Someli-admin-api, someli-api, and someli-dashboard-be connections. Total against the shared DB likely exceeds 100. Verify the MySQL max_connections setting to ensure headroom.

Hardcoded cron schedules

Cron expressions are hardcoded in each file. No central schedule registry. Adjusting frequency requires editing the file and redeploying.

Risk patterns

  1. forEach over async workcategoryData.forEach(c => { ... await ... }) doesn't await in the iteration. Some jobs may complete too early or run out of order. Spot-check each.
  2. String-interpolated SQLcon.query(\SELECT * FROM tIndustries WHERE id = ${i.id}`)`. Most IDs are integers from previous queries, so safe; but the pattern is fragile.
  3. Unbounded LIMIT — some queries lack LIMIT; on a growing DB they get slower.
  4. No retry / dead-letter queue — a failed run logs to console and waits for the next cron tick.

Recommendations

ID Recommendation Effort
J-1 Replace the 30+ industry-clone jobs with one parameterised job_industry_generator.js driven by a tJobConfig table Medium (1-2 weeks)
J-2 Add ecosystem.config.js declaring all jobs as separate PM2 apps with log paths Small (1 day)
J-3 Add a distributed lock (Redis SET NX EX or MySQL SELECT … FOR UPDATE NOWAIT on a tJobLock table) Medium (a few days)
J-4 Add slow-query timing to sync-mysql calls; warn on > 500 ms Small (a few hours)
J-5 Move from sync-mysql to mysql2/promise in jobs (eliminates event-loop blocking) Medium (per-job effort)
J-6 Add a retry / dead-letter pattern for failed iterations Medium
J-7 Verify MySQL max_connections and connection-pool sizing across all platform services Small (ops task)