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.jsand run inside the same Node process (verify bygrep -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¶
forEachover async work —categoryData.forEach(c => { ... await ... })doesn't await in the iteration. Some jobs may complete too early or run out of order. Spot-check each.- String-interpolated SQL —
con.query(\SELECT * FROM tIndustries WHERE id = ${i.id}`)`. Most IDs are integers from previous queries, so safe; but the pattern is fragile. - Unbounded
LIMIT— some queries lackLIMIT; on a growing DB they get slower. - 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) |