Changeset - 3c46e2bb7736
[Not reviewed]
0 2 0
Lance Edgar (lance) - 3 years ago 2022-02-01 13:38:47
lance@edbob.org
Allow rattail watcher to get deleted before new/dirty changes

in particular this hopefully helps rattail -> rattail for certain edge
cases, but we'll see...
2 files changed with 61 insertions and 36 deletions:
0 comments (0 inline, 0 general)
rattail/datasync/rattail.py
Show inline comments
 
@@ -2,7 +2,7 @@
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2021 Lance Edgar
 
#  Copyright © 2010-2022 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -30,7 +30,6 @@ import logging
 

	
 
from sqlalchemy import orm
 

	
 
from rattail.db import Session, model
 
from rattail.db.util import make_topo_sortkey
 
from rattail.datasync import DataSyncWatcher, DataSyncConsumer, DataSyncImportConsumer
 
from rattail.config import parse_list
 
@@ -56,7 +55,8 @@ class RattailWatcher(DataSyncWatcher):
 
        Rattail database, to see if there are any pending changes for
 
        the datasync daemon.
 
        """
 
        session = Session(bind=self.engine)
 
        model = self.model
 
        session = self.app.make_session(bind=self.engine)
 
        changes = session.query(model.Change).all()
 
        session.expunge_all()
 
        session.close()
 
@@ -69,33 +69,28 @@ class RattailWatcher(DataSyncWatcher):
 
        class_names = sorted(set([c.class_name for c in changes]))
 
        class_names.sort(key=self.topo_sortkey)
 

	
 
        # now sort the changes per our ordered class sequence.  note
 
        # that we must first add new/dirty changes only, then we add
 
        # deleted at the end.  so that deletes are processed last, by
 
        # the consumer(s), to hopefully avoid dependency issues
 
        final = []
 

	
 
        # new/dirty
 
        for class_name in class_names:
 
            for change in [c for c in changes
 
                           if c.class_name == class_name
 
                           and not c.deleted]:
 
                final.append((change.uuid,
 
                              model.DataSyncChange(
 
                                  payload_type=class_name,
 
                                  payload_key=change.instance_uuid,
 
                                  deletion=change.deleted)))
 

	
 
        # deleted
 
        for class_name in class_names:
 
            for change in [c for c in changes
 
                           if c.class_name == class_name
 
                           and c.deleted]:
 
                final.append((change.uuid,
 
                              model.DataSyncChange(
 
                                  payload_type=class_name,
 
                                  payload_key=change.instance_uuid,
 
                                  deletion=change.deleted)))
 
        # collect datasync changes for new/dirty and deleted
 
        dirty = self.get_new_dirty(changes, class_names)
 
        deleted = self.get_deleted(changes, class_names)
 

	
 
        # we traditionally have processed new/dirty first, then
 
        # deleted.  but config can reverse that, to test new logic
 
        # which is meant to help with one particular scenario: when a
 
        # record with "unique code" is effectively moved; the new
 
        # record can't be created until the old is deleted
 
        # TODO: once "deleted first" has been tested, should make it
 
        # the default, unless there is some reason not to...
 
        # TODO: original logic comments said: note that we must first
 
        # add new/dirty changes only, then we add deleted at the end.
 
        # so that deletes are processed last, by the consumer(s), to
 
        # hopefully avoid dependency issues
 
        deleted_first = self.config.getbool(
 
            'rattail.datasync', 'rattail_watcher_deleted_first',
 
            default=False)
 
        if deleted_first:
 
            final = deleted + dirty
 
        else:
 
            final = dirty + deleted
 

	
 
        # when a Product has a price relationship change (e.g. it is
 
        # given or loses the "current price"), the Product record is
 
@@ -114,9 +109,38 @@ class RattailWatcher(DataSyncWatcher):
 

	
 
        return final
 

	
 
    def get_new_dirty(self, changes, class_names):
 
        model = self.model
 
        result = []
 
        for class_name in class_names:
 
            for change in [c for c in changes
 
                           if c.class_name == class_name
 
                           and not c.deleted]:
 
                result.append((change.uuid,
 
                               model.DataSyncChange(
 
                                   payload_type=class_name,
 
                                   payload_key=change.instance_uuid,
 
                                   deletion=change.deleted)))
 
        return result
 

	
 
    def get_deleted(self, changes, class_names):
 
        model = self.model
 
        result = []
 
        for class_name in class_names:
 
            for change in [c for c in changes
 
                           if c.class_name == class_name
 
                           and c.deleted]:
 
                result.append((change.uuid,
 
                               model.DataSyncChange(
 
                                   payload_type=class_name,
 
                                   payload_key=change.instance_uuid,
 
                                   deletion=change.deleted)))
 
        return result
 

	
 
    def prune_changes(self, keys):
 
        model = self.model
 
        deleted = 0
 
        session = Session(bind=self.engine)
 
        session = self.app.make_session(bind=self.engine)
 
        for key in keys:
 
            if key: # note that key can sometimes be None
 
                change = session.query(model.Change).get(key)
 
@@ -171,7 +195,7 @@ class RattailConsumer(DataSyncConsumer):
 
        class_names = sorted(class_names)
 
        class_names.sort(key=self.topo_sortkey)
 

	
 
        session = Session(bind=self.engine)
 
        session = self.app.make_session(bind=self.engine)
 
        if self.runas_username:
 
            session.set_continuum_user(self.runas_username)
 

	
 
@@ -399,7 +423,7 @@ class FromRattailToRattailExportConsumer(FromRattailToRattailBase):
 
        self.model = self.config.get_model()
 

	
 
    def make_target_session(self):
 
        return Session(bind=self.target_engine)
 
        return self.app.make_session(bind=self.target_engine)
 

	
 
    def process_changes(self, session, changes):
 
        target_session = self.make_target_session()
 
@@ -434,8 +458,8 @@ class FromRattailToRattailImportConsumer(FromRattailToRattailBase):
 
        self.model = self.config.get_model()
 

	
 
    def process_changes(self, session, changes):
 
        self.host_session = Session(bind=self.host_engine)
 
        local_session = Session(bind=self.local_engine)
 
        self.host_session = self.app.make_session(bind=self.host_engine)
 
        local_session = self.app.make_session(bind=self.local_engine)
 
        if self.runas_username:
 
            local_session.set_continuum_user(self.runas_username)
 

	
rattail/datasync/watchers.py
Show inline comments
 
@@ -2,7 +2,7 @@
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2021 Lance Edgar
 
#  Copyright © 2010-2022 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -57,6 +57,7 @@ class DataSyncWatcher(object):
 
        self.dbkey = dbkey
 
        self.delay = 1 # seconds
 
        self.model = self.config.get_model()
 
        self.app = self.config.get_app()
 

	
 
    def setup(self):
 
        """
0 comments (0 inline, 0 general)