Skip to content

Instantly share code, notes, and snippets.

@kylemclaren
Forked from comerford/killLongRunningOps.js
Last active January 30, 2026 07:12
Show Gist options
  • Select an option

  • Save kylemclaren/3c09a4dda5991cf0bf9c to your computer and use it in GitHub Desktop.

Select an option

Save kylemclaren/3c09a4dda5991cf0bf9c to your computer and use it in GitHub Desktop.
Find and (safely) kill long running MongoDB ops
db.currentOp().inprog.forEach(
function(op) {
if(op.secs_running > 5) printjson(op);
}
)
// kills long running ops in MongoDB (taking seconds as an arg to define "long")
// attempts to be a bit safer than killing all by excluding replication related operations
// and only targeting queries as opposed to commands etc.
killLongRunningOps = function(maxSecsRunning) {
currOp = db.currentOp();
for (oper in currOp.inprog) {
op = currOp.inprog[oper-0];
if (op.secs_running > maxSecsRunning && op.op == "query" && !op.ns.startsWith("local")) {
print("Killing opId: " + op.opid
+ " running for over secs: "
+ op.secs_running);
db.killOp(op.opid);
}
}
};
//example: killLongRunningOps(5)
@paravz
Copy link

paravz commented Oct 7, 2020

Thanks for this, i ended up using a modified version:

db.currentOp().inprog.forEach(                                                                                         
  function(op) {                                                                                                       
      if(op.secs_running > 5 )                                                                                        
                  print("host: " + op.host                                                                             
                    + " secs_running: " + op.secs_running                                                              
                    + " op: " + op.op                                                                                  
                    + " ns: " + op.ns                                                                                  
                    + " command: " + JSON.stringify(op.command)                                                        
                 );                                                                                                    
  }                                                                                                                    
);

@mehboobpatel
Copy link

Hi i am a devops engineer, and using mongodb percona in replicaset wondering if this is a production ready script cz i guess we should ignore system queries like .local admin cmd and configs also what about index builds operations here is my script let me know if you guys can test it for edge cases

`#!/usr/bin/env python3

import argparse
import sys
import re
from pymongo import MongoClient
from pymongo.errors import PyMongoError

def parse_args():
parser = argparse.ArgumentParser(
description="Safely kill long-running MongoDB operations (production-ready)"
)
parser.add_argument(
"--uri",
required=True,
help="MongoDB connection string (mongodb+srv://...)"
)
parser.add_argument(
"--seconds",
type=int,
required=True,
help="Kill operations running longer than this many seconds"
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Only print operations, do not kill"
)
parser.add_argument(
"--max-kills",
type=int,
default=3,
help="Maximum operations to kill per run (default: 3)"
)
return parser.parse_args()

def main():
args = parse_args()

print("[INFO] MongoDB long-op killer starting")
print(f"[INFO] Threshold   : {args.seconds}s")
print(f"[INFO] Dry run     : {args.dry_run}")
print(f"[INFO] Max kills   : {args.max_kills}")

try:
    client = MongoClient(args.uri, serverSelectionTimeoutMS=5000)
    admin_db = client.admin

    # --- ENSURE PRIMARY ---
    hello = admin_db.command("hello")
    if not hello.get("isWritablePrimary"):
        print("[INFO] Node is not PRIMARY, exiting safely")
        return 0

    # --- FETCH LONG RUNNING OPS ---
    current_ops = admin_db.command(
        "currentOp",
        {
            "active": True,
            "secs_running": {"$gte": args.seconds},
            "op": {"$ne": "none"}
        }
    )

    inprog = current_ops.get("inprog", [])

    if not inprog:
        print("[INFO] No long-running operations found")
        return 0

    print(f"[INFO] Found {len(inprog)} candidate operations")

    killed = 0

    for op in inprog:
        if killed >= args.max_kills:
            print("[INFO] Kill limit reached, stopping")
            break

        ns = op.get("ns", "")
        desc = op.get("desc", "")
        secs = op.get("secs_running", 0)
        opid = op.get("opid")
        client_addr = op.get("client", "unknown")

        # --- SAFETY FILTERS ---
        if not ns:
            continue

        if ns.startswith(("local.", "config.", "admin.")):
            continue

        if re.search("Balancer", desc):
            continue
        # ----------------------

        print(
            f"[OP] opid={opid} "
            f"secs={secs} "
            f"ns={ns} "
            f"client={client_addr}"
        )

        if args.dry_run:
            continue

        try:
            admin_db.command("killOp", op=opid)
            killed += 1
            print(f"[KILLED] opid={opid}")
        except PyMongoError as e:
            print(f"[ERROR] Failed to kill opid={opid}: {e}")

    print(f"[INFO] Completed. Ops killed: {killed}")
    return 0

except PyMongoError as e:
    print(f"[FATAL] MongoDB error: {e}")
    return 1
except Exception as e:
    print(f"[FATAL] Unexpected error: {e}")
    return 1

if name == "main":
sys.exit(main())
`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment