0

We use multiprocessing to send a newsletter via Sendinblue to our ~750k users. Sometimes a process does not complete.

observation of the problem

when I run the script several times in test mode (without sending the emails), with the same dataset.

In 80% of attempts it works correctly.

In 20% of attempts, a process never ends, which blocks the continuity of the script.

Here is the part of the script that has responsibility for the processes

def loop_on_targets(targets: list, sender_mode: dict) -> None:
    """
    Loop on each customer, get email dataset and send newsletters
    :param targets: list
    customers
    :param sender_mode:
    senders object for Sendinblue, switch country
    :return:
    """

    if len(targets) == 0:
        return

    # common counter for stdout
    global i
    manager = Manager()
    shared_list = manager.list()

    # jobs limit
    jobs = []
    started_jobs = 0
    max_jobs = 200

    # START PROCESS LOOP
    # for each customer get email content data from database in separated process
    for process in [
                       Process(target=send_proxymities,
                               args=(BORROWER_QUERY.format(
                                   i_owner[5],
                                   i_owner[4],
                                   'ROLE_OWNER',
                                   i_owner[5],
                                   i_owner[4]
                               ), i_owner, 'ROLE_OWNER', shared_list)) for i_owner in
                       list(filter((lambda o: 'ROLE_OWNER' in o[2]), targets))] + [
                       Process(target=send_proxymities,
                               args=(OWNER_QUERY.format(
                                   i_borrower[5],
                                   i_borrower[4],
                                   'ROLE_BORROWER',
                                   i_borrower[5],
                                   i_borrower[4]
                               ), i_borrower, 'ROLE_BORROWER', shared_list)) for i_borrower in
                       list(filter((lambda b: 'ROLE_BORROWER' in b[2]), targets))
                   ]:

        # increment counter for follow-up of processed customers on stdout
        i += 1

        jobs.append(process)
        process.start()
        started_jobs += 1

        # jobs limit management
        if started_jobs >= max_jobs:
            while started_jobs >= max_jobs:
                started_jobs = 0
                for job in jobs:
                    started_jobs += job.is_alive()

        # email sending management
        if len(shared_list) >= 90:
            print("Send " + str(len(shared_list)))

            # sending by group of 90 customers to respect the limit of the smtp service
            for chunk in [shared_list[idx: idx + 90] for idx in range(0, len(shared_list), 90)]:
                # send email on separated thread
                threading.Thread(target=asyncio.run, args=(send_email_by_curl(list(chunk), sender_mode),)).start()

            # clear shared list
            shared_list[:] = []

            # END PROCESS LOOP

    # wait if processes are not terminated
    for p in jobs:
        p.join()

    # email sending management
    if len(shared_list) > 0:
        print("Send " + str(len(shared_list)))

        # sending by group of 90 customers to respect the limit of the smtp service
        for chunk in [shared_list[idx: idx + 90] for idx in range(0, len(shared_list), 90)]:
            # send email on separated thread
            threading.Thread(target=asyncio.run, args=(send_email_by_curl(list(chunk), sender_mode),)).start()
J.M EMT
  • 1
  • 1

0 Answers0