2014-09-08 3 views
0

Немного фона:Python синхронизации между потоками и процессами

Я пишу функцию в Django, чтобы получить следующий номер счета, который должен быть последовательным (не пробелы), поэтому функция выглядит следующим образом:

def get_next_invoice_number(): 
    """ 
    Returns the max(invoice_number) + 1 from the payment records 
    Does NOT pre-allocate number 
    """ 
    # TODO ensure this is thread safe 
    max_num = Payment.objects.aggregate(Max('invoice_number'))['invoice_number__max'] 
    if max_num is not None: 
     return max_num + 1 
    return PaymentConfig.min_invoice_number 

Теперь проблема, эта функция только возвращает max()+1, в моей среде у меня есть несколько процессов Django, поэтому, если эта функция вызывается дважды для 2-х различных платежей (до первой записи сохранены), они получат тот же номер счета-фактуры.

Чтобы устранить эту проблему, я могу переопределить функцию save(), чтобы вызвать get_next_invoice_number(), чтобы свести к минимуму промежуток времени между этими вызовами функций, но по-прежнему существует очень малая вероятность возникновения проблемы.

Так что я хочу осуществить блокировку в методе одобрять, что-то вроде

from multiprocessing import Lock 
lock = Lock() 

class Payment(models.Model): 
    def approve(self): 
     lock.acquire() 
     try: 
      self.invoice_number = get_next_invoice_number() 
      self.save() 
     except: 
      pass 
     finally: 
      lock.release() 

Так что мои вопросы:

  1. ли это выглядеть хорошо?
  2. Замок предназначен для многопроцессорности, как насчет потоков?

UPDATE:

  1. Как мой коллега отметил, что это не будет работать, когда он развернут на нескольких серверах, замки не будут иметь смысла.
  2. Похоже, что блокировка транзакций БД - это путь.
+0

1.) Почему это должно быть последовательным? 2.) Почему вы не можете предварительно идентифицировать идентификатор счета на чеке (т. Е. Используя функцию автоматического прироста DB)? Никаких дубликатов. – Andy

+0

@ Andy это требование бухгалтерского учета для номера счета-фактуры, которое будет последовательным. –

ответ

0

Самый простой способ сделать это, безусловно, с помощью существующих инструментов вашей базы данных для создания последовательностей. На самом деле, если вы не против значения, начиная с 1, вы можете просто использовать Django's AutoField.

Если ваши бизнес-требования таковы, что вам нужно выбрать стартовый номер, вам нужно будет посмотреть, как это сделать в базе данных. Вот somequestions, которые могут помочь.

Попытка обеспечить это с помощью замков или транзакций будет сложнее выполнять и выполнять медленнее.

Смежные вопросы