[VOL-3828] subscriber flow remove fails in ATT scenario due to adverse sequence of flow add/del, + slight changes for MDS check

Signed-off-by: mpagenko <michael.pagenkopf@adtran.com>
Change-Id: I9071896f5b6ab1f99f65847d46f94b351dec38a6
diff --git a/internal/pkg/onuadaptercore/device_handler.go b/internal/pkg/onuadaptercore/device_handler.go
index 0f86675..c55afd3 100644
--- a/internal/pkg/onuadaptercore/device_handler.go
+++ b/internal/pkg/onuadaptercore/device_handler.go
@@ -104,19 +104,19 @@
 	cL2PmFsm
 )
 
-type idleCheckStruct struct {
-	idleCheckFunc func(*deviceHandler, context.Context, string) bool
-	idleState     string
+type omciIdleCheckStruct struct {
+	omciIdleCheckFunc func(*deviceHandler, context.Context, usedOmciConfigFsms, string) bool
+	omciIdleState     string
 }
 
-var fsmIdleStateFuncMap = map[usedOmciConfigFsms]idleCheckStruct{
-	cUploadFsm:        {(*deviceHandler).mibUploadFsmInIdleState, cMibUlFsmIdleState},
-	cDownloadFsm:      {(*deviceHandler).mibDownloadFsmInIdleState, cMibDlFsmIdleState},
-	cUniLockFsm:       {(*deviceHandler).devUniLockFsmInIdleState, cUniFsmIdleState},
-	cUniUnLockFsm:     {(*deviceHandler).devUniUnlockFsmInIdleState, cUniFsmIdleState},
-	cAniConfigFsm:     {(*deviceHandler).devAniConfigFsmInIdleState, cAniFsmIdleState},
-	cUniVlanConfigFsm: {(*deviceHandler).devUniVlanConfigFsmInIdleState, cVlanFsmIdleState},
-	cL2PmFsm:          {(*deviceHandler).l2PmFsmInIdleState, cL2PmFsmIdleState},
+var fsmOmciIdleStateFuncMap = map[usedOmciConfigFsms]omciIdleCheckStruct{
+	cUploadFsm:        {(*deviceHandler).isFsmInOmciIdleStateDefault, cMibUlFsmIdleState},
+	cDownloadFsm:      {(*deviceHandler).isFsmInOmciIdleStateDefault, cMibDlFsmIdleState},
+	cUniLockFsm:       {(*deviceHandler).isFsmInOmciIdleStateDefault, cUniFsmIdleState},
+	cUniUnLockFsm:     {(*deviceHandler).isFsmInOmciIdleStateDefault, cUniFsmIdleState},
+	cAniConfigFsm:     {(*deviceHandler).isAniConfigFsmInOmciIdleState, cAniFsmIdleState},
+	cUniVlanConfigFsm: {(*deviceHandler).isUniVlanConfigFsmInOmciIdleState, cVlanFsmIdleState},
+	cL2PmFsm:          {(*deviceHandler).isFsmInOmciIdleStateDefault, cL2PmFsmIdleState},
 }
 
 const (
@@ -200,7 +200,8 @@
 	stopAlarmManager           chan bool
 	stopHeartbeatCheck         chan bool
 	uniEntityMap               map[uint32]*onuUniPort
-	lockVlanConfig             sync.Mutex
+	mutexKvStoreContext        sync.Mutex
+	lockVlanConfig             sync.RWMutex
 	UniVlanConfigFsmMap        map[uint8]*UniVlanConfigFsm
 	reconciling                bool
 	mutexReconcilingFlag       sync.RWMutex
@@ -231,7 +232,7 @@
 	//dh.metrics = pmmetrics.NewPmMetrics(cloned.Id, pmmetrics.Frequency(150), pmmetrics.FrequencyOverride(false), pmmetrics.Grouped(false), pmmetrics.Metrics(pmNames))
 	//TODO initialize the support classes.
 	dh.uniEntityMap = make(map[uint32]*onuUniPort)
-	dh.lockVlanConfig = sync.Mutex{}
+	dh.lockVlanConfig = sync.RWMutex{}
 	dh.UniVlanConfigFsmMap = make(map[uint8]*UniVlanConfigFsm)
 	dh.reconciling = false
 	dh.chReconcilingFinished = make(chan bool)
@@ -322,7 +323,6 @@
 	// with restricted output of 16(?) bytes would be ...omciMsg.Message[:16]
 	logger.Debugw(ctx, "inter-adapter-recv-omci", log.Fields{
 		"device-id": dh.deviceID, "RxOmciMessage": hex.EncodeToString(omciMsg.Message)})
-	//receive_message(omci_msg.message)
 	pDevEntry := dh.getOnuDeviceEntry(ctx, true)
 	if pDevEntry != nil {
 		if pDevEntry.PDevOmciCC != nil {
@@ -848,7 +848,6 @@
 	}
 	pDevEntry.persUniConfigMutex.RLock()
 	defer pDevEntry.persUniConfigMutex.RUnlock()
-
 	if len(pDevEntry.sOnuPersistentData.PersUniConfig) == 0 {
 		logger.Debugw(ctx, "reconciling - no uni-configs have been stored before adapter restart - terminate reconcilement",
 			log.Fields{"device-id": dh.deviceID})
@@ -873,13 +872,16 @@
 		for _, flowData := range uniData.PersFlowParams {
 			logger.Debugw(ctx, "add flow with cookie slice", log.Fields{"device-id": dh.deviceID, "cookies": flowData.CookieSlice})
 			//the slice can be passed 'by value' here, - which internally passes its reference copy
+			dh.lockVlanConfig.RLock()
 			if _, exist = dh.UniVlanConfigFsmMap[uniData.PersUniID]; exist {
 				if err := dh.UniVlanConfigFsmMap[uniData.PersUniID].SetUniFlowParams(ctx, flowData.VlanRuleParams.TpID,
 					flowData.CookieSlice, uint16(flowData.VlanRuleParams.MatchVid), uint16(flowData.VlanRuleParams.SetVid),
 					uint8(flowData.VlanRuleParams.SetPcp)); err != nil {
 					logger.Errorw(ctx, err.Error(), log.Fields{"device-id": dh.deviceID})
 				}
+				dh.lockVlanConfig.RUnlock()
 			} else {
+				dh.lockVlanConfig.RUnlock()
 				if err := dh.createVlanFilterFsm(ctx, uniPort, flowData.VlanRuleParams.TpID, flowData.CookieSlice,
 					uint16(flowData.VlanRuleParams.MatchVid), uint16(flowData.VlanRuleParams.SetVid),
 					uint8(flowData.VlanRuleParams.SetPcp), OmciVlanFilterAddDone); err != nil {
@@ -968,8 +970,6 @@
 		"device-id": dh.deviceID, "image-name": (*apImageDsc).Name})
 	//return success to comfort the core processing during integration
 	return nil
-	// TODO!!: also verify error response behavior
-	//return fmt.Errorf("onuSwUpgrade not yet implemented in deviceHandler: %s", dh.deviceID)
 }
 
 //  deviceHandler methods that implement the adapters interface requests## end #########
@@ -1576,7 +1576,9 @@
 		}
 		for _, uniPort := range dh.uniEntityMap {
 			// reset the possibly existing VlanConfigFsm
+			dh.lockVlanConfig.RLock()
 			if pVlanFilterFsm, exist := dh.UniVlanConfigFsmMap[uniPort.uniID]; exist {
+				dh.lockVlanConfig.RUnlock()
 				//VlanFilterFsm exists and was already started
 				pVlanFilterStatemachine := pVlanFilterFsm.pAdaptFsm.pFsm
 				if pVlanFilterStatemachine != nil {
@@ -1586,6 +1588,8 @@
 					//and reset the UniVlanConfig FSM
 					_ = pVlanFilterStatemachine.Event(vlanEvReset)
 				}
+			} else {
+				dh.lockVlanConfig.RUnlock()
 			}
 		}
 	}
@@ -1850,7 +1854,7 @@
 	// attention: the device reason update is done based on ONU-UNI-Port related activity
 	//  - which may cause some inconsistency
 
-	if aDevEvent == OmciVlanFilterAddDone {
+	if aDevEvent == OmciVlanFilterAddDone || aDevEvent == OmciVlanFilterAddDoneNoKvStore {
 		if dh.deviceReason != drOmciFlowsPushed {
 			// which may be the case from some previous actvity on another UNI Port of the ONU
 			// or even some previous flow add activity on the same port
@@ -1865,9 +1869,16 @@
 			_ = dh.deviceReasonUpdate(ctx, drOmciFlowsDeleted, true)
 		}
 	}
-	if err := dh.storePersistentData(ctx); err != nil {
-		logger.Warnw(ctx, "store persistent data error - continue for now as there will be additional write attempts",
-			log.Fields{"device-id": dh.deviceID, "err": err})
+
+	if aDevEvent == OmciVlanFilterAddDone || aDevEvent == OmciVlanFilterRemDone {
+		//events that request KvStore write
+		if err := dh.storePersistentData(ctx); err != nil {
+			logger.Warnw(ctx, "store persistent data error - continue for now as there will be additional write attempts",
+				log.Fields{"device-id": dh.deviceID, "err": err})
+		}
+	} else {
+		logger.Debugw(ctx, "OmciVlanFilter*Done* - write to KvStore not requested",
+			log.Fields{"device-id": dh.deviceID})
 	}
 }
 
@@ -1902,7 +1913,7 @@
 		{
 			dh.processOmciAniConfigDoneEvent(ctx, devEvent)
 		}
-	case OmciVlanFilterAddDone, OmciVlanFilterRemDone:
+	case OmciVlanFilterAddDone, OmciVlanFilterAddDoneNoKvStore, OmciVlanFilterRemDone, OmciVlanFilterRemDoneNoKvStore:
 		{
 			dh.processOmciVlanFilterDoneEvent(ctx, devEvent)
 		}
@@ -2296,13 +2307,15 @@
 	}
 
 	//mutex protection as the update_flow rpc maybe running concurrently for different flows, perhaps also activities
-	dh.lockVlanConfig.Lock()
-	defer dh.lockVlanConfig.Unlock()
+	dh.lockVlanConfig.RLock()
 	logger.Debugw(ctx, "flow-add got lock", log.Fields{"device-id": dh.deviceID})
 	if _, exist := dh.UniVlanConfigFsmMap[apUniPort.uniID]; exist {
-		return dh.UniVlanConfigFsmMap[apUniPort.uniID].SetUniFlowParams(ctx, loTpID, loCookieSlice,
+		err := dh.UniVlanConfigFsmMap[apUniPort.uniID].SetUniFlowParams(ctx, loTpID, loCookieSlice,
 			loMatchVlan, loSetVlan, loSetPcp)
+		dh.lockVlanConfig.RUnlock()
+		return err
 	}
+	dh.lockVlanConfig.RUnlock()
 	return dh.createVlanFilterFsm(ctx, apUniPort, loTpID, loCookieSlice,
 		loMatchVlan, loSetVlan, loSetPcp, OmciVlanFilterAddDone)
 }
@@ -2337,8 +2350,8 @@
 	*/
 
 	//mutex protection as the update_flow rpc maybe running concurrently for different flows, perhaps also activities
-	dh.lockVlanConfig.Lock()
-	defer dh.lockVlanConfig.Unlock()
+	dh.lockVlanConfig.RLock()
+	defer dh.lockVlanConfig.RUnlock()
 	if _, exist := dh.UniVlanConfigFsmMap[apUniPort.uniID]; exist {
 		return dh.UniVlanConfigFsmMap[apUniPort.uniID].RemoveUniFlowParams(ctx, loCookie)
 	}
@@ -2367,7 +2380,9 @@
 		pDevEntry.pOnuDB, aTpID, aDevEvent, "UniVlanConfigFsm", chVlanFilterFsm,
 		dh.pOpenOnuAc.AcceptIncrementalEvto, aCookieSlice, aMatchVlan, aSetVlan, aSetPcp)
 	if pVlanFilterFsm != nil {
+		dh.lockVlanConfig.Lock()
 		dh.UniVlanConfigFsmMap[apUniPort.uniID] = pVlanFilterFsm
+		dh.lockVlanConfig.Unlock()
 		pVlanFilterStatemachine := pVlanFilterFsm.pAdaptFsm.pFsm
 		if pVlanFilterStatemachine != nil {
 			if pVlanFilterStatemachine.Is(vlanStDisabled) {
@@ -2422,7 +2437,10 @@
 	//TODO!! verify and start pending flow configuration
 	//some pending config request my exist in case the UniVlanConfig FSM was already started - with internal data -
 	//but execution was set to 'on hold' as first the TechProfile config had to be applied
+
+	dh.lockVlanConfig.RLock()
 	if pVlanFilterFsm, exist := dh.UniVlanConfigFsmMap[apUniPort.uniID]; exist {
+		dh.lockVlanConfig.RUnlock()
 		//VlanFilterFsm exists and was already started (assumed to wait for TechProfile execution here)
 		pVlanFilterStatemachine := pVlanFilterFsm.pAdaptFsm.pFsm
 		if pVlanFilterStatemachine != nil {
@@ -2462,7 +2480,9 @@
 			logger.Debugw(ctx, "UniVlanConfigFsm StateMachine does not exist, no flow processing", log.Fields{
 				"device-id": dh.deviceID, "UniPort": apUniPort.portNo})
 		}
-	} // else: nothing to do
+	} else {
+		dh.lockVlanConfig.RUnlock()
+	}
 }
 
 //RemoveVlanFilterFsm deletes the stored pointer to the VlanConfigFsm
@@ -2471,7 +2491,9 @@
 	logger.Debugw(ctx, "remove UniVlanConfigFsm StateMachine", log.Fields{
 		"device-id": dh.deviceID, "uniPort": apUniPort.portNo})
 	//save to do, even if entry dows not exist
+	dh.lockVlanConfig.Lock()
 	delete(dh.UniVlanConfigFsmMap, apUniPort.uniID)
+	dh.lockVlanConfig.Unlock()
 }
 
 //ProcessPendingTpDelete processes any pending TP delete (if available)
@@ -2503,10 +2525,30 @@
 	}
 }
 
+//startWritingOnuDataToKvStore initiates the KVStore write of ONU persistent data
+func (dh *deviceHandler) startWritingOnuDataToKvStore(ctx context.Context, aPDevEntry *OnuDeviceEntry) error {
+	dh.mutexKvStoreContext.Lock()         //this write routine may (could) be called with the same context,
+	defer dh.mutexKvStoreContext.Unlock() //this write routine may (could) be called with the same context,
+	// obviously then parallel processing on the cancel must be avoided
+	// deadline context to ensure completion of background routines waited for
+	//20200721: 10s proved to be less in 8*8 ONU test on local vbox machine with debug, might be further adapted
+	deadline := time.Now().Add(dh.pOpenOnuAc.maxTimeoutInterAdapterComm) //allowed run time to finish before execution
+	dctx, cancel := context.WithDeadline(context.Background(), deadline)
+
+	aPDevEntry.resetKvProcessingErrorIndication()
+	var wg sync.WaitGroup
+	wg.Add(1) // for the 1 go routine to finish
+
+	go aPDevEntry.updateOnuKvStore(log.WithSpanFromContext(dctx, ctx), &wg)
+	dh.waitForCompletion(ctx, cancel, &wg, "UpdateKvStore") //wait for background process to finish
+
+	return aPDevEntry.getKvProcessingErrorIndication()
+}
+
 //storePersUniFlowConfig updates local storage of OnuUniFlowConfig and writes it into kv-store afterwards to have it
 //available for potential reconcilement
-
-func (dh *deviceHandler) storePersUniFlowConfig(ctx context.Context, aUniID uint8, aUniVlanFlowParams *[]uniVlanFlowParams) error {
+func (dh *deviceHandler) storePersUniFlowConfig(ctx context.Context, aUniID uint8,
+	aUniVlanFlowParams *[]uniVlanFlowParams, aWriteToKvStore bool) error {
 
 	if dh.isReconciling() {
 		logger.Debugw(ctx, "reconciling - don't store persistent UniFlowConfig", log.Fields{"device-id": dh.deviceID})
@@ -2521,19 +2563,10 @@
 	}
 	pDevEntry.updateOnuUniFlowConfig(aUniID, aUniVlanFlowParams)
 
-	// deadline context to ensure completion of background routines waited for
-	//20200721: 10s proved to be less in 8*8 ONU test on local vbox machine with debug, might be further adapted
-	deadline := time.Now().Add(dh.pOpenOnuAc.maxTimeoutInterAdapterComm) //allowed run time to finish before execution
-	dctx, cancel := context.WithDeadline(context.Background(), deadline)
-
-	pDevEntry.resetKvProcessingErrorIndication()
-	var wg sync.WaitGroup
-	wg.Add(1) // for the 1 go routine to finish
-
-	go pDevEntry.updateOnuKvStore(log.WithSpanFromContext(dctx, ctx), &wg)
-	dh.waitForCompletion(ctx, cancel, &wg, "UpdateKvStore") //wait for background process to finish
-
-	return pDevEntry.getKvProcessingErrorIndication()
+	if aWriteToKvStore {
+		return dh.startWritingOnuDataToKvStore(ctx, pDevEntry)
+	}
+	return nil
 }
 
 func (dh *deviceHandler) waitForCompletion(ctx context.Context, cancel context.CancelFunc, wg *sync.WaitGroup, aCallerIdent string) {
@@ -2566,21 +2599,7 @@
 		logger.Warnw(ctx, "No valid OnuDevice", log.Fields{"device-id": dh.deviceID})
 		return fmt.Errorf("no valid OnuDevice: %s", dh.deviceID)
 	}
-	deadline := time.Now().Add(dh.pOpenOnuAc.maxTimeoutInterAdapterComm) //allowed run time to finish before execution
-	dctx, cancel := context.WithDeadline(context.Background(), deadline)
-
-	pDevEntry.resetKvProcessingErrorIndication()
-	var wg sync.WaitGroup
-	wg.Add(1) // for the 1 go routine to finish
-
-	go pDevEntry.updateOnuKvStore(dctx, &wg)
-	dh.waitForCompletion(ctx, cancel, &wg, "UpdateKvStore") //wait for background process to finish
-
-	if err := pDevEntry.getKvProcessingErrorIndication(); err != nil {
-		logger.Warnw(ctx, "KV-processing error", log.Fields{"device-id": dh.deviceID, "err": err})
-		return err
-	}
-	return nil
+	return dh.startWritingOnuDataToKvStore(ctx, pDevEntry)
 }
 
 func (dh *deviceHandler) combineErrorStrings(errS ...error) error {
@@ -2779,78 +2798,95 @@
 	return portStatus.getUniPortStatus(ctx, uniInfo.UniIndex)
 }
 
-func (dh *deviceHandler) isFsmInState(ctx context.Context, pFsm *fsm.FSM, wantedState string) bool {
-	var currentState string
-	if pFsm != nil {
-		currentState = pFsm.Current()
-		if currentState == wantedState {
-			return true
-		}
-	} else {
-		logger.Warnw(ctx, "FSM not defined!", log.Fields{"wantedState": wantedState, "device-id": dh.deviceID})
+func (dh *deviceHandler) isFsmInOmciIdleState(ctx context.Context, pFsm *fsm.FSM, wantedState string) bool {
+	if pFsm == nil {
+		return true //FSM not active - so there is no activity on omci
 	}
-	return false
+	return pFsm.Current() == wantedState
 }
 
-func (dh *deviceHandler) mibUploadFsmInIdleState(ctx context.Context, idleState string) bool {
-	return dh.isFsmInState(ctx, dh.pOnuOmciDevice.pMibUploadFsm.pFsm, idleState)
-}
-
-func (dh *deviceHandler) mibDownloadFsmInIdleState(ctx context.Context, idleState string) bool {
-	return dh.isFsmInState(ctx, dh.pOnuOmciDevice.pMibDownloadFsm.pFsm, idleState)
-}
-
-func (dh *deviceHandler) devUniLockFsmInIdleState(ctx context.Context, idleState string) bool {
-	return dh.isFsmInState(ctx, dh.pLockStateFsm.pAdaptFsm.pFsm, idleState)
-}
-
-func (dh *deviceHandler) devUniUnlockFsmInIdleState(ctx context.Context, idleState string) bool {
-	return dh.isFsmInState(ctx, dh.pUnlockStateFsm.pAdaptFsm.pFsm, idleState)
-}
-
-func (dh *deviceHandler) devAniConfigFsmInIdleState(ctx context.Context, idleState string) bool {
-	if dh.pOnuTP.pAniConfigFsm != nil {
-		for _, v := range dh.pOnuTP.pAniConfigFsm {
-			if !dh.isFsmInState(ctx, v.pAdaptFsm.pFsm, idleState) {
-				return false
+func (dh *deviceHandler) isFsmInOmciIdleStateDefault(ctx context.Context, omciFsm usedOmciConfigFsms, wantedState string) bool {
+	var pFsm *fsm.FSM
+	//note/TODO!!: might be that access to all these specific FSM; pointers need a semaphore protection as well, cmp lockUpgradeFsm
+	switch omciFsm {
+	case cUploadFsm:
+		{
+			pFsm = dh.pOnuOmciDevice.pMibUploadFsm.pFsm
+		}
+	case cDownloadFsm:
+		{
+			pFsm = dh.pOnuOmciDevice.pMibDownloadFsm.pFsm
+		}
+	case cUniLockFsm:
+		{
+			pFsm = dh.pLockStateFsm.pAdaptFsm.pFsm
+		}
+	case cUniUnLockFsm:
+		{
+			pFsm = dh.pUnlockStateFsm.pAdaptFsm.pFsm
+		}
+	case cL2PmFsm:
+		{
+			if dh.pOnuMetricsMgr != nil && dh.pOnuMetricsMgr.pAdaptFsm != nil {
+				pFsm = dh.pOnuMetricsMgr.pAdaptFsm.pFsm
+			} else {
+				return true //FSM not active - so there is no activity on omci
 			}
 		}
-		return true
-	}
-	logger.Warnw(ctx, "AniConfig FSM not defined!", log.Fields{"device-id": dh.deviceID})
-	return false
-}
-
-func (dh *deviceHandler) devUniVlanConfigFsmInIdleState(ctx context.Context, idleState string) bool {
-	if dh.UniVlanConfigFsmMap != nil {
-		for _, v := range dh.UniVlanConfigFsmMap {
-			if !dh.isFsmInState(ctx, v.pAdaptFsm.pFsm, idleState) {
-				return false
-			}
+	default:
+		{
+			logger.Errorw(ctx, "invalid stateMachine selected for idle check", log.Fields{
+				"device-id": dh.deviceID, "selectedFsm number": omciFsm})
+			return false //logical error in FSM check, do not not indicate 'idle' - we can't be sure
 		}
-		return true
 	}
-	logger.Warnw(ctx, "UniVlanConfig FSM not defined!", log.Fields{"device-id": dh.deviceID})
-	return false
+	return dh.isFsmInOmciIdleState(ctx, pFsm, wantedState)
 }
 
-func (dh *deviceHandler) l2PmFsmInIdleState(ctx context.Context, idleState string) bool {
-	if dh.pOnuMetricsMgr != nil && dh.pOnuMetricsMgr.pAdaptFsm != nil && dh.pOnuMetricsMgr.pAdaptFsm.pFsm != nil {
-		return dh.isFsmInState(ctx, dh.pOnuMetricsMgr.pAdaptFsm.pFsm, idleState)
-	}
-	logger.Warnw(ctx, "L2 PM FSM not defined!", log.Fields{"device-id": dh.deviceID})
-	return false
-}
-
-func (dh *deviceHandler) allButCallingFsmInIdleState(ctx context.Context, callingFsm usedOmciConfigFsms) bool {
-	for fsmName, fsmStruct := range fsmIdleStateFuncMap {
-		if fsmName != callingFsm && !fsmStruct.idleCheckFunc(dh, ctx, fsmStruct.idleState) {
+func (dh *deviceHandler) isAniConfigFsmInOmciIdleState(ctx context.Context, omciFsm usedOmciConfigFsms, idleState string) bool {
+	for _, v := range dh.pOnuTP.pAniConfigFsm {
+		if !dh.isFsmInOmciIdleState(ctx, v.pAdaptFsm.pFsm, idleState) {
 			return false
 		}
 	}
 	return true
 }
 
+func (dh *deviceHandler) isUniVlanConfigFsmInOmciIdleState(ctx context.Context, omciFsm usedOmciConfigFsms, idleState string) bool {
+	dh.lockVlanConfig.RLock()
+	defer dh.lockVlanConfig.RUnlock()
+	for _, v := range dh.UniVlanConfigFsmMap {
+		if !dh.isFsmInOmciIdleState(ctx, v.pAdaptFsm.pFsm, idleState) {
+			return false
+		}
+	}
+	return true //FSM not active - so there is no activity on omci
+}
+
+func (dh *deviceHandler) checkUserServiceExists(ctx context.Context) bool {
+	dh.lockVlanConfig.RLock()
+	defer dh.lockVlanConfig.RUnlock()
+	for _, v := range dh.UniVlanConfigFsmMap {
+		if v.pAdaptFsm.pFsm != nil {
+			if v.pAdaptFsm.pFsm.Is(cVlanFsmConfiguredState) {
+				return true //there is at least one VLAN FSM with some active configuration
+			}
+		}
+	}
+	return false //there is no VLAN FSM with some active configuration
+}
+
+func (dh *deviceHandler) checkAuditStartCondition(ctx context.Context, callingFsm usedOmciConfigFsms) bool {
+	for fsmName, fsmStruct := range fsmOmciIdleStateFuncMap {
+		if fsmName != callingFsm && !fsmStruct.omciIdleCheckFunc(dh, ctx, fsmName, fsmStruct.omciIdleState) {
+			return false
+		}
+	}
+	// a further check is done to identify, if at least some data traffic related configuration exists
+	// so that a user of this ONU could be 'online' (otherwise it makes no sense to check the MDS [with the intention to keep the user service up])
+	return dh.checkUserServiceExists(ctx)
+}
+
 func (dh *deviceHandler) prepareReconcilingWithActiveAdapter(ctx context.Context) {
 	logger.Debugw(ctx, "prepare to reconcile the ONU with adapter using persistency data", log.Fields{"device-id": dh.device.Id})
 	if err := dh.resetFsms(ctx, false); err != nil {
diff --git a/internal/pkg/onuadaptercore/mib_sync.go b/internal/pkg/onuadaptercore/mib_sync.go
index eb7dce6..0fcf7f9 100644
--- a/internal/pkg/onuadaptercore/mib_sync.go
+++ b/internal/pkg/onuadaptercore/mib_sync.go
@@ -269,10 +269,10 @@
 
 func (oo *OnuDeviceEntry) enterAuditingState(ctx context.Context, e *fsm.Event) {
 	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start MibAudit processing in State": e.FSM.Current(), "device-id": oo.deviceID})
-	if oo.baseDeviceHandler.allButCallingFsmInIdleState(ctx, cUploadFsm) {
+	if oo.baseDeviceHandler.checkAuditStartCondition(ctx, cUploadFsm) {
 		oo.requestMdsValue(ctx)
 	} else {
-		logger.Debugw(ctx, "MibSync FSM", log.Fields{"Configuration is ongoing - skip auditing!": e.FSM.Current(), "device-id": oo.deviceID})
+		logger.Debugw(ctx, "MibSync FSM", log.Fields{"Configuration is ongoing or missing - skip auditing!": e.FSM.Current(), "device-id": oo.deviceID})
 		go func() {
 			_ = oo.pMibUploadFsm.pFsm.Event(ulEvSuccess)
 		}()
@@ -281,10 +281,10 @@
 
 func (oo *OnuDeviceEntry) enterReAuditingState(ctx context.Context, e *fsm.Event) {
 	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start retest MdsValue processing in State": e.FSM.Current(), "device-id": oo.deviceID})
-	if oo.baseDeviceHandler.allButCallingFsmInIdleState(ctx, cUploadFsm) {
+	if oo.baseDeviceHandler.checkAuditStartCondition(ctx, cUploadFsm) {
 		oo.requestMdsValue(ctx)
 	} else {
-		logger.Debugw(ctx, "MibSync FSM", log.Fields{"Configuration is ongoing - skip re-auditing!": e.FSM.Current(), "device-id": oo.deviceID})
+		logger.Debugw(ctx, "MibSync FSM", log.Fields{"Configuration is ongoing or missing - skip re-auditing!": e.FSM.Current(), "device-id": oo.deviceID})
 		go func() {
 			_ = oo.pMibUploadFsm.pFsm.Event(ulEvSuccess)
 		}()
diff --git a/internal/pkg/onuadaptercore/omci_vlan_config.go b/internal/pkg/onuadaptercore/omci_vlan_config.go
index 6725098..cfec8e8 100644
--- a/internal/pkg/onuadaptercore/omci_vlan_config.go
+++ b/internal/pkg/onuadaptercore/omci_vlan_config.go
@@ -36,6 +36,7 @@
 
 const (
 	// internal predefined values
+	cWaitForCookieDeletion = 3 //seconds
 	cDefaultDownstreamMode = 0
 	cDefaultTpid           = 0x8100
 	cVtfdTableSize         = 12             //as per G.988
@@ -43,6 +44,14 @@
 )
 
 const (
+	//  internal offsets for requestEvent according to definition in onu_device_entry::OnuDeviceEvent
+	cDeviceEventOffsetAddWithKvStore    = 0 //OmciVlanFilterAddDone - OmciVlanFilterAddDone cannot use because of lint
+	cDeviceEventOffsetAddNoKvStore      = OmciVlanFilterAddDoneNoKvStore - OmciVlanFilterAddDone
+	cDeviceEventOffsetRemoveWithKvStore = OmciVlanFilterRemDone - OmciVlanFilterAddDone
+	cDeviceEventOffsetRemoveNoKvStore   = OmciVlanFilterRemDoneNoKvStore - OmciVlanFilterAddDone
+)
+
+const (
 	// bit mask offsets for EVTOCD VlanTaggingOperationTable related to 32 bits (4 bytes)
 	cFilterPrioOffset      = 28
 	cFilterVidOffset       = 15
@@ -110,7 +119,8 @@
 	vlanStCleanupDone     = "vlanStCleanupDone"
 	vlanStResetting       = "vlanStResetting"
 )
-const cVlanFsmIdleState = vlanStConfigDone
+const cVlanFsmIdleState = vlanStConfigDone       // state where no OMCI activity is done (for a longer time)
+const cVlanFsmConfiguredState = vlanStConfigDone // state that indicates that at least some valid user related VLAN configuration should exist
 
 type uniVlanRuleParams struct {
 	TpID         uint8  `json:"tp_id"`
@@ -145,6 +155,7 @@
 	acceptIncrementalEvtoOption bool
 	clearPersistency            bool
 	mutexFlowParams             sync.RWMutex
+	chCookieDeleted             chan bool //channel to indicate that a specificly indicated cookie was deleted
 	actualUniVlanConfigRule     uniVlanRuleParams
 	uniVlanFlowParamsSlice      []uniVlanFlowParams
 	uniRemoveFlowsSlice         []uniRemoveVlanFlowParams
@@ -157,6 +168,8 @@
 	pLastTxMeInstance           *me.ManagedEntity
 	requestEventOffset          uint8
 	TpIDWaitingFor              uint8
+	//cookie value that indicates that a rule to add is delayed by waiting for deletion of some other existing rule with the same cookie
+	delayNewRuleCookie uint64
 }
 
 //NewUniVlanConfigFsm is the 'constructor' for the state machine to config the PON ANI ports
@@ -298,7 +311,7 @@
 
 	//permanently store flow config for reconcile case
 	if err := oFsm.pDeviceHandler.storePersUniFlowConfig(ctx, oFsm.pOnuUniPort.uniID,
-		&oFsm.uniVlanFlowParamsSlice); err != nil {
+		&oFsm.uniVlanFlowParamsSlice, true); err != nil {
 		logger.Errorw(ctx, err.Error(), log.Fields{"device-id": oFsm.deviceID})
 		return err
 	}
@@ -365,9 +378,9 @@
 
 	flowEntryMatch := false
 	flowCookieModify := false
+	requestAppendRule := false
 	//mutex protection is required for possible concurrent access to FSM members
 	oFsm.mutexFlowParams.Lock()
-	defer oFsm.mutexFlowParams.Unlock()
 	for flow, storedUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
 		//TODO: Verify if using e.g. hashes for the structures here for comparison may generate
 		//  countable run time optimization (perhaps with including the hash in kvStore storage?)
@@ -387,18 +400,41 @@
 					}
 				}
 				if !cookieMatch {
-					logger.Debugw(ctx, "UniVlanConfigFsm flow setting -adding new cookie", log.Fields{
-						"device-id": oFsm.deviceID, "cookie": newCookie})
-					//as range works with copies of the slice we have to write to the original slice!!
-					oFsm.uniVlanFlowParamsSlice[flow].CookieSlice = append(oFsm.uniVlanFlowParamsSlice[flow].CookieSlice,
-						newCookie)
-					flowCookieModify = true
+					delayedCookie := oFsm.delayNewRuleForCookie(ctx, aCookieSlice)
+					if delayedCookie != 0 {
+						//a delay for adding the cookie to this rule is requested
+						// take care of the mutex which is already locked here, need to unlock/lock accordingly to prevent deadlock in suspension
+						oFsm.mutexFlowParams.Unlock()
+						oFsm.suspendNewRule(ctx)
+						flowCookieModify, requestAppendRule = oFsm.reviseFlowConstellation(ctx, delayedCookie, loRuleParams)
+						oFsm.mutexFlowParams.Lock()
+					} else {
+						logger.Debugw(ctx, "UniVlanConfigFsm flow setting -adding new cookie", log.Fields{
+							"device-id": oFsm.deviceID, "cookie": newCookie})
+						//as range works with copies of the slice we have to write to the original slice!!
+						oFsm.uniVlanFlowParamsSlice[flow].CookieSlice = append(oFsm.uniVlanFlowParamsSlice[flow].CookieSlice,
+							newCookie)
+						flowCookieModify = true
+					}
 				}
 			} //for all new cookies
 			break // found rule - no further rule search
 		}
 	}
-	if !flowEntryMatch { //it is a new rule
+	oFsm.mutexFlowParams.Unlock()
+
+	if !flowEntryMatch { //it is (was) a new rule
+		delayedCookie := oFsm.suspendIfRequiredNewRule(ctx, aCookieSlice)
+		requestAppendRule = true //default assumption here is that rule is to be appended
+		flowCookieModify = true  //and that the the flow data base is to be updated
+		if delayedCookie != 0 {  //it was suspended
+			flowCookieModify, requestAppendRule = oFsm.reviseFlowConstellation(ctx, delayedCookie, loRuleParams)
+		}
+	}
+	kvStoreWrite := false //default setting is to not write to kvStore immediately - will be done on FSM execution finally
+	if requestAppendRule {
+		oFsm.mutexFlowParams.Lock()
+		defer oFsm.mutexFlowParams.Unlock()
 		if oFsm.numUniFlows < cMaxAllowedFlows {
 			loFlowParams := uniVlanFlowParams{VlanRuleParams: loRuleParams}
 			loFlowParams.CookieSlice = make([]uint64, 0)
@@ -458,6 +494,7 @@
 		}
 	} else {
 		// no activity within the FSM for OMCI processing, the deviceReason may be updated immediately
+		kvStoreWrite = true // ensure actual data write to kvStore immediately (no FSM activity)
 		if oFsm.numUniFlows == oFsm.configuredUniFlow {
 			//all requested rules really have been configured
 			// state transition notification is checked in deviceHandler
@@ -465,7 +502,8 @@
 				//also the related TechProfile was already configured
 				logger.Debugw(ctx, "UniVlanConfigFsm rule already set - send immediate add-success event for reason update", log.Fields{
 					"device-id": oFsm.deviceID})
-				go oFsm.pDeviceHandler.deviceProcStatusUpdate(ctx, oFsm.requestEvent)
+				// success indication without the need to write to kvStore (done already below with updated data from storePersUniFlowConfig())
+				go oFsm.pDeviceHandler.deviceProcStatusUpdate(ctx, OnuDeviceEvent(oFsm.requestEvent+cDeviceEventOffsetAddNoKvStore))
 			}
 		} else {
 			//  avoid device reason update as the rule config connected to this flow may still be in progress
@@ -476,9 +514,10 @@
 		}
 	}
 
-	if !flowEntryMatch || flowCookieModify { // some change was done to the flow entries
+	if flowCookieModify { // some change was done to the flow entries
 		//permanently store flow config for reconcile case
-		if err := oFsm.pDeviceHandler.storePersUniFlowConfig(ctx, oFsm.pOnuUniPort.uniID, &oFsm.uniVlanFlowParamsSlice); err != nil {
+		if err := oFsm.pDeviceHandler.storePersUniFlowConfig(ctx, oFsm.pOnuUniPort.uniID,
+			&oFsm.uniVlanFlowParamsSlice, kvStoreWrite); err != nil {
 			logger.Errorw(ctx, err.Error(), log.Fields{"device-id": oFsm.deviceID})
 			return err
 		}
@@ -486,21 +525,111 @@
 	return nil
 }
 
+// VOL-3828 flow config sequence workaround ###########  start ##########
+func (oFsm *UniVlanConfigFsm) delayNewRuleForCookie(ctx context.Context, aCookieSlice []uint64) uint64 {
+	//assumes mutexFlowParams.Lock() protection from caller!
+	if oFsm.delayNewRuleCookie == 0 && len(aCookieSlice) == 1 {
+		// if not already waiting, limitation for this workaround is to just have one overlapping cookie/rule
+		// suspend check is done only of there is only one cookie in the request
+		//  background: more elements only expected in reconcile use case, where no conflicting sequence is to be expected
+		newCookie := aCookieSlice[0]
+		for _, storedUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
+			for _, cookie := range storedUniFlowParams.CookieSlice {
+				if cookie == newCookie {
+					logger.Debugw(ctx, "UniVlanConfigFsm flow setting - new cookie still exists for some rule", log.Fields{
+						"device-id": oFsm.deviceID, "cookie": cookie, "exists with SetVlan": storedUniFlowParams.VlanRuleParams.SetVid})
+					oFsm.delayNewRuleCookie = newCookie
+					return newCookie //found new cookie in some existing rule
+				}
+			} // for all stored cookies of the actual inspected rule
+		} //for all rules
+	}
+	return 0 //no delay requested
+}
+func (oFsm *UniVlanConfigFsm) suspendNewRule(ctx context.Context) {
+	oFsm.mutexFlowParams.RLock()
+	logger.Infow(ctx, "Need to suspend adding this rule as long as the cookie is still connected to some other rule", log.Fields{
+		"device-id": oFsm.deviceID, "cookie": oFsm.delayNewRuleCookie})
+	oFsm.mutexFlowParams.RUnlock()
+	select {
+	case <-oFsm.chCookieDeleted:
+		logger.Infow(ctx, "resume adding this rule after having deleted cookie in some other rule", log.Fields{
+			"device-id": oFsm.deviceID, "cookie": oFsm.delayNewRuleCookie})
+	case <-time.After(time.Duration(cWaitForCookieDeletion) * time.Second):
+		logger.Errorw(ctx, "timeout waiting for deletion of cookie in some other rule, just try to continue", log.Fields{
+			"device-id": oFsm.deviceID, "cookie": oFsm.delayNewRuleCookie})
+	}
+	oFsm.mutexFlowParams.Lock()
+	oFsm.delayNewRuleCookie = 0
+	oFsm.mutexFlowParams.Unlock()
+}
+func (oFsm *UniVlanConfigFsm) suspendIfRequiredNewRule(ctx context.Context, aCookieSlice []uint64) uint64 {
+	oFsm.mutexFlowParams.Lock()
+	delayedCookie := oFsm.delayNewRuleForCookie(ctx, aCookieSlice)
+	oFsm.mutexFlowParams.Unlock()
+
+	if delayedCookie != 0 {
+		oFsm.suspendNewRule(ctx)
+	}
+	return delayedCookie
+}
+
+//returns flowModified, RuleAppendRequest
+func (oFsm *UniVlanConfigFsm) reviseFlowConstellation(ctx context.Context, aCookie uint64, aUniVlanRuleParams uniVlanRuleParams) (bool, bool) {
+	flowEntryMatch := false
+	oFsm.mutexFlowParams.Lock()
+	defer oFsm.mutexFlowParams.Unlock()
+	for flow, storedUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
+		if storedUniFlowParams.VlanRuleParams == aUniVlanRuleParams {
+			flowEntryMatch = true
+			logger.Debugw(ctx, "UniVlanConfigFsm flow revise - rule already exists", log.Fields{
+				"device-id": oFsm.deviceID})
+			cookieMatch := false
+			for _, cookie := range storedUniFlowParams.CookieSlice {
+				if cookie == aCookie {
+					logger.Debugw(ctx, "UniVlanConfigFsm flow revise - and cookie already exists", log.Fields{
+						"device-id": oFsm.deviceID, "cookie": cookie})
+					cookieMatch = true
+					break //found new cookie - no further search for this requested cookie
+				}
+			}
+			if !cookieMatch {
+				logger.Debugw(ctx, "UniVlanConfigFsm flow revise -adding new cookie", log.Fields{
+					"device-id": oFsm.deviceID, "cookie": aCookie})
+				//as range works with copies of the slice we have to write to the original slice!!
+				oFsm.uniVlanFlowParamsSlice[flow].CookieSlice = append(oFsm.uniVlanFlowParamsSlice[flow].CookieSlice,
+					aCookie)
+				return true, false //flowModified, NoRuleAppend
+			}
+			break // found rule - no further rule search
+		}
+	}
+	if !flowEntryMatch { //it is a new rule
+		return true, true //flowModified, RuleAppend
+	}
+	return false, false //flowNotModified, NoRuleAppend
+}
+
+// VOL-3828 flow config sequence workaround ###########  end ##########
+
 //RemoveUniFlowParams verifies on existence of flow cookie,
 // if found removes cookie from flow cookie list and if this is empty
 // initiates removal of the flow related configuration from the ONU (via OMCI)
 func (oFsm *UniVlanConfigFsm) RemoveUniFlowParams(ctx context.Context, aCookie uint64) error {
+	var deletedCookie uint64
 	flowCookieMatch := false
 	//mutex protection is required for possible concurrent access to FSM members
 	oFsm.mutexFlowParams.Lock()
 	defer oFsm.mutexFlowParams.Unlock()
+remove_loop:
 	for flow, storedUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
 		for i, cookie := range storedUniFlowParams.CookieSlice {
 			if cookie == aCookie {
 				logger.Debugw(ctx, "UniVlanConfigFsm flow removal - cookie found", log.Fields{
 					"device-id": oFsm.deviceID, "cookie": cookie})
 				flowCookieMatch = true
-
+				deletedCookie = aCookie
+				kvStoreWrite := false //default setting is to not write to kvStore immediately - will be done on FSM execution finally
 				//remove the cookie from the cookie slice and verify it is getting empty
 				if len(storedUniFlowParams.CookieSlice) == 1 {
 					logger.Debugw(ctx, "UniVlanConfigFsm flow removal - full flow removal", log.Fields{
@@ -509,7 +638,7 @@
 					//create a new element for the removeVlanFlow slice
 					loRemoveParams := uniRemoveVlanFlowParams{
 						vlanRuleParams: storedUniFlowParams.VlanRuleParams,
-						cookie:         storedUniFlowParams.CookieSlice[0],
+						cookie:         aCookie,
 					}
 					oFsm.uniRemoveFlowsSlice = append(oFsm.uniRemoveFlowsSlice, loRemoveParams)
 
@@ -537,22 +666,8 @@
 							oFsm.uniVlanFlowParamsSlice[:flow], oFsm.uniVlanFlowParamsSlice[flow+1:]...)
 						//here we have to check, if there are still other flows referencing to the actual ProfileId
 						//  before we can request that this profile gets deleted before a new flow add is allowed
-						tpIDInOtherFlows := false
-						for _, tpUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
-							if tpUniFlowParams.VlanRuleParams.TpID == usedTpID {
-								tpIDInOtherFlows = true
-								break // search loop can be left
-							}
-						}
-						if tpIDInOtherFlows {
-							logger.Debugw(ctx, "UniVlanConfigFsm tp-id used in deleted flow is still used in other flows", log.Fields{
-								"device-id": oFsm.deviceID, "tp-id": usedTpID})
-						} else {
-							logger.Debugw(ctx, "UniVlanConfigFsm tp-id used in deleted flow is not used anymore", log.Fields{
-								"device-id": oFsm.deviceID, "tp-id": usedTpID})
-							//request that this profile gets deleted before a new flow add is allowed
-							oFsm.pUniTechProf.setProfileToDelete(oFsm.pOnuUniPort.uniID, usedTpID, true)
-						}
+						//  (needed to extract to function due to lint complexity)
+						oFsm.updateTechProfileToDelete(ctx, usedTpID)
 						logger.Debugw(ctx, "UniVlanConfigFsm flow removal - specific flow removed from data", log.Fields{
 							"device-id": oFsm.deviceID})
 					}
@@ -576,29 +691,34 @@
 						oFsm.uniVlanFlowParamsSlice[flow].CookieSlice[:i],
 						oFsm.uniVlanFlowParamsSlice[flow].CookieSlice[i+1:]...)
 					// no activity within the FSM for OMCI processing, the deviceReason may be updated immediately
+					kvStoreWrite = true // ensure actual data write to kvStore immediately (no FSM activity)
 					// state transition notification is checked in deviceHandler
 					if oFsm.pDeviceHandler != nil {
-						//making use of the add->remove successor enum assumption/definition
-						go oFsm.pDeviceHandler.deviceProcStatusUpdate(ctx, OnuDeviceEvent(uint8(oFsm.requestEvent)+1))
+						// success indication without the need to write to kvStore (done already below with updated data from storePersUniFlowConfig())
+						go oFsm.pDeviceHandler.deviceProcStatusUpdate(ctx, OnuDeviceEvent(oFsm.requestEvent+cDeviceEventOffsetRemoveNoKvStore))
 					}
 					logger.Debugw(ctx, "UniVlanConfigFsm flow removal - rule persists with still valid cookies", log.Fields{
 						"device-id": oFsm.deviceID, "cookies": oFsm.uniVlanFlowParamsSlice[flow].CookieSlice})
+					if deletedCookie == oFsm.delayNewRuleCookie {
+						//the delayedNewCookie is the one that is currently deleted, but the rule still exist with other cookies
+						//as long as there are further cookies for this rule indicate there is still some cookie to be deleted
+						//simply use the first one
+						oFsm.delayNewRuleCookie = oFsm.uniVlanFlowParamsSlice[flow].CookieSlice[0]
+						logger.Debugw(ctx, "UniVlanConfigFsm remaining cookie awaited for deletion before new rule add", log.Fields{
+							"device-id": oFsm.deviceID, "cookie": oFsm.delayNewRuleCookie})
+					}
 				}
-
 				//permanently store the modified flow config for reconcile case
 				if oFsm.pDeviceHandler != nil {
-					if err := oFsm.pDeviceHandler.storePersUniFlowConfig(ctx, oFsm.pOnuUniPort.uniID, &oFsm.uniVlanFlowParamsSlice); err != nil {
+					if err := oFsm.pDeviceHandler.storePersUniFlowConfig(ctx, oFsm.pOnuUniPort.uniID,
+						&oFsm.uniVlanFlowParamsSlice, kvStoreWrite); err != nil {
 						logger.Errorw(ctx, err.Error(), log.Fields{"device-id": oFsm.deviceID})
 						return err
 					}
 				}
-
-				break //found the cookie - no further search for this requested cookie
+				break remove_loop //found the cookie - no further search for this requested cookie
 			}
 		}
-		if flowCookieMatch { //cookie already found: no need for further search in other flows
-			break
-		}
 	} //search all flows
 	if !flowCookieMatch { //some cookie remove-request for a cookie that does not exist in the FSM data
 		logger.Warnw(ctx, "UniVlanConfigFsm flow removal - remove-cookie not found", log.Fields{
@@ -607,8 +727,8 @@
 		// no activity within the FSM for OMCI processing, the deviceReason may be updated immediately
 		// state transition notification is checked in deviceHandler
 		if oFsm.pDeviceHandler != nil {
-			//making use of the add->remove successor enum assumption/definition
-			go oFsm.pDeviceHandler.deviceProcStatusUpdate(ctx, OnuDeviceEvent(uint8(oFsm.requestEvent)+1))
+			// success indication without the need to write to kvStore (no change)
+			go oFsm.pDeviceHandler.deviceProcStatusUpdate(ctx, OnuDeviceEvent(oFsm.requestEvent+cDeviceEventOffsetRemoveNoKvStore))
 		}
 		return nil
 	} //unknown cookie
@@ -616,6 +736,27 @@
 	return nil
 }
 
+func (oFsm *UniVlanConfigFsm) updateTechProfileToDelete(ctx context.Context, usedTpID uint8) {
+	//here we have to check, if there are still other flows referencing to the actual ProfileId
+	//  before we can request that this profile gets deleted before a new flow add is allowed
+	tpIDInOtherFlows := false
+	for _, tpUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
+		if tpUniFlowParams.VlanRuleParams.TpID == usedTpID {
+			tpIDInOtherFlows = true
+			break // search loop can be left
+		}
+	}
+	if tpIDInOtherFlows {
+		logger.Debugw(ctx, "UniVlanConfigFsm tp-id used in deleted flow is still used in other flows", log.Fields{
+			"device-id": oFsm.deviceID, "tp-id": usedTpID})
+	} else {
+		logger.Debugw(ctx, "UniVlanConfigFsm tp-id used in deleted flow is not used anymore", log.Fields{
+			"device-id": oFsm.deviceID, "tp-id": usedTpID})
+		//request that this profile gets deleted before a new flow add is allowed
+		oFsm.pUniTechProf.setProfileToDelete(oFsm.pOnuUniPort.uniID, usedTpID, true)
+	}
+}
+
 func (oFsm *UniVlanConfigFsm) enterConfigStarting(ctx context.Context, e *fsm.Event) {
 	logger.Debugw(ctx, "UniVlanConfigFsm start", log.Fields{"in state": e.FSM.Current(),
 		"device-id": oFsm.deviceID})
@@ -623,6 +764,7 @@
 	// this FSM is not intended for re-start, needs always new creation for a new run
 	// (self-destroying - compare enterDisabled())
 	oFsm.omciMIdsResponseReceived = make(chan bool)
+	oFsm.chCookieDeleted = make(chan bool)
 	// start go routine for processing of LockState messages
 	go oFsm.processOmciVlanMessages(ctx)
 	//let the state machine run forward from here directly
@@ -727,7 +869,7 @@
 func (oFsm *UniVlanConfigFsm) enterConfigEvtocd(ctx context.Context, e *fsm.Event) {
 	logger.Debugw(ctx, "UniVlanConfigFsm - start config EVTOCD loop", log.Fields{
 		"in state": e.FSM.Current(), "device-id": oFsm.deviceID})
-	oFsm.requestEventOffset = 0 //0 offset for last flow-add activity
+	oFsm.requestEventOffset = uint8(cDeviceEventOffsetAddWithKvStore) //0 offset for last flow-add activity
 	go func() {
 		//using the first element in the slice because it's the first flow per definition here
 		errEvto := oFsm.performConfigEvtocdEntries(ctx, 0)
@@ -756,7 +898,7 @@
 	oFsm.mutexFlowParams.RLock()
 	defer oFsm.mutexFlowParams.RUnlock()
 
-	logger.Debugw(ctx, "UniVlanConfigFsm - checking on more flows", log.Fields{
+	logger.Infow(ctx, "UniVlanConfigFsm config done - checking on more flows", log.Fields{
 		"in state": e.FSM.Current(), "device-id": oFsm.deviceID,
 		"overall-uni-rules": oFsm.numUniFlows, "configured-uni-rules": oFsm.configuredUniFlow})
 	pConfigVlanStateAFsm := oFsm.pAdaptFsm
@@ -918,7 +1060,7 @@
 			return
 		}
 	}
-	oFsm.requestEventOffset = 0 //0 offset for last flow-add activity
+	oFsm.requestEventOffset = uint8(cDeviceEventOffsetAddWithKvStore) //0 offset for last flow-add activity
 	go func() {
 		tpID := oFsm.actualUniVlanConfigRule.TpID
 		errEvto := oFsm.performConfigEvtocdEntries(ctx, oFsm.configuredUniFlow)
@@ -1076,10 +1218,12 @@
 	} else {
 		logger.Warnw(ctx, "UniVlanConfigFsm - tp id not available", log.Fields{"device-id": oFsm.deviceID})
 	}
-	logger.Debugw(ctx, "UniVlanConfigFsm - removing the removal data", log.Fields{
-		"in state": e.FSM.Current(), "device-id": oFsm.deviceID})
-
 	oFsm.mutexFlowParams.Lock()
+	deletedCookie := oFsm.uniRemoveFlowsSlice[0].cookie
+	logger.Debugw(ctx, "UniVlanConfigFsm - removing the removal data", log.Fields{
+		"in state": e.FSM.Current(), "device-id": oFsm.deviceID,
+		"removed cookie": deletedCookie, "waitForDeleteCookie": oFsm.delayNewRuleCookie})
+
 	if len(oFsm.uniRemoveFlowsSlice) <= 1 {
 		oFsm.uniRemoveFlowsSlice = nil //reset the slice
 		logger.Debugw(ctx, "UniVlanConfigFsm flow removal - last remove-flow deleted", log.Fields{
@@ -1094,7 +1238,7 @@
 	}
 	oFsm.mutexFlowParams.Unlock()
 
-	oFsm.requestEventOffset = 1 //offset 1 for last flow-remove activity
+	oFsm.requestEventOffset = uint8(cDeviceEventOffsetRemoveWithKvStore) //offset for last flow-remove activity (with kvStore request)
 	//return to the basic config verification state
 	pConfigVlanStateAFsm := oFsm.pAdaptFsm
 	if pConfigVlanStateAFsm != nil {
@@ -1108,6 +1252,15 @@
 
 	oFsm.mutexFlowParams.RLock()
 	noOfFlowRem := len(oFsm.uniRemoveFlowsSlice)
+	if deletedCookie == oFsm.delayNewRuleCookie {
+		// flush the channel CookieDeleted to ensure it is not lingering from some previous (aborted) activity
+		select {
+		case <-oFsm.chCookieDeleted:
+			logger.Debug(ctx, "flushed CookieDeleted")
+		default:
+		}
+		oFsm.chCookieDeleted <- true // let the waiting AddFlow thread continue
+	}
 	oFsm.mutexFlowParams.RUnlock()
 	// If all pending flow removes are completed and TP ID is valid, processing any pending TP delete
 	if noOfFlowRem == 0 && tpID > 0 {
@@ -1154,11 +1307,15 @@
 			//permanently remove possibly stored persistent data
 			if len(oFsm.uniVlanFlowParamsSlice) > 0 {
 				var emptySlice = make([]uniVlanFlowParams, 0)
-				_ = oFsm.pDeviceHandler.storePersUniFlowConfig(ctx, oFsm.pOnuUniPort.uniID, &emptySlice) //ignore errors
+				_ = oFsm.pDeviceHandler.storePersUniFlowConfig(ctx, oFsm.pOnuUniPort.uniID, &emptySlice, true) //ignore errors
 			}
 		} else {
 			logger.Debugw(ctx, "UniVlanConfigFsm persistency data not cleared", log.Fields{"device-id": oFsm.deviceID})
 		}
+		if oFsm.delayNewRuleCookie != 0 {
+			// looks like the waiting AddFlow is stuck
+			oFsm.chCookieDeleted <- true // let the waiting AddFlow thread continue/treminate
+		}
 		oFsm.mutexFlowParams.RUnlock()
 		//request removal of 'reference' in the Handler (completely clear the FSM and its data)
 		go oFsm.pDeviceHandler.RemoveVlanFilterFsm(ctx, oFsm.pOnuUniPort)
diff --git a/internal/pkg/onuadaptercore/onu_device_entry.go b/internal/pkg/onuadaptercore/onu_device_entry.go
index e01de18..816ebcd 100644
--- a/internal/pkg/onuadaptercore/onu_device_entry.go
+++ b/internal/pkg/onuadaptercore/onu_device_entry.go
@@ -146,10 +146,14 @@
 	OmciAniConfigDone
 	// OmciAniResourceRemoved - AniSide TechProfile related resource (Gem/TCont) removed
 	OmciAniResourceRemoved // needs to be the successor of OmciAniConfigDone!
-	// OmciVlanFilterAddDone - Omci Vlan config done according to flow-add
+	// OmciVlanFilterAddDone - Omci Vlan config done according to flow-add with request to write kvStore
 	OmciVlanFilterAddDone
-	// OmciVlanFilterRemDone - Omci Vlan config done according to flow-remove
-	OmciVlanFilterRemDone // needs to be the successor of OmciVlanFilterAddDone!
+	// OmciVlanFilterAddDoneNoKvStore - Omci Vlan config done according to flow-add without writing kvStore
+	OmciVlanFilterAddDoneNoKvStore // needs to be the successor of OmciVlanFilterAddDone!
+	// OmciVlanFilterRemDone - Omci Vlan config done according to flow-remove with request to write kvStore
+	OmciVlanFilterRemDone // needs to be the successor of OmciVlanFilterAddDoneNoKvStore!
+	// OmciVlanFilterRemDoneNoKvStore - Omci Vlan config done according to flow-remove without writing kvStore
+	OmciVlanFilterRemDoneNoKvStore // needs to be the successor of OmciVlanFilterRemDone!
 	// Add other events here as needed (alarms separate???)
 )