private long ProcessLoadCommand(InstancePersistenceContext context, LoadWorkflowCommand command) { Owner owner = CheckOwner(context, command.Name); Instance instance = PersistenceItemManager.Load <Instance>(context.InstanceView.InstanceId); if (instance == null) { // Checking instance.Owner is like an InstanceLockQueryResult. context.QueriedInstanceStore(new InstanceLockQueryResult(context.InstanceView.InstanceId, Guid.Empty)); if (context.InstanceView.IsBoundToLock) { context.InstanceHandle.Free(); throw new InstanceLockLostException(command.Name, context.InstanceView.InstanceId); } if (!command.AcceptUninitializedInstance) { throw new InstanceNotReadyException(command.Name, context.InstanceView.InstanceId); } instance = new Instance() { Version = 1, Id = context.InstanceView.InstanceId, Owner = context.InstanceView.InstanceOwner.InstanceOwnerId, Metadata = new PropertyBag() }; PersistenceItemManager.SaveToFile <Instance>(instance); context.BindAcquiredLock(1); } else { // Checking instance.Owner is like an InstanceLockQueryResult. context.QueriedInstanceStore(new InstanceLockQueryResult(context.InstanceView.InstanceId, instance.Owner)); if (context.InstanceView.IsBoundToLock) { if (instance.Version != context.InstanceVersion || instance.Owner != context.InstanceView.InstanceOwner.InstanceOwnerId) { if (context.InstanceVersion > instance.Version) { throw new InvalidProgramException("This is a bug, the context should never be bound higher than the lock."); } context.InstanceHandle.Free(); throw new InstanceLockLostException(command.Name, context.InstanceView.InstanceId); } } if (instance.State == InstanceState.Completed) { throw new InstanceCompleteException(command.Name, context.InstanceView.InstanceId); } if ((instance.Data == null || instance.Data.Count < 1) && !command.AcceptUninitializedInstance) { throw new InstanceNotReadyException(command.Name, context.InstanceView.InstanceId); } if (!context.InstanceView.IsBoundToLock) { if (instance.Owner == Guid.Empty) { instance.Version++; instance.Owner = context.InstanceView.InstanceOwner.InstanceOwnerId; PersistenceItemManager.SaveToFile <Instance>(instance); context.BindAcquiredLock(instance.Version); } else if (instance.Owner == context.InstanceView.InstanceOwner.InstanceOwnerId) { // This is the very interesting parallel-convoy conflicting handle race resolution case. Two handles // can get bound to the same lock, which is necessary to allow parallel convoy to succeed without preventing // zombied locked instances from being reclaimed. return(instance.Version); } else { throw new InstanceLockedException(command.Name, instance.Owner); } } } Dictionary <Guid, IDictionary <XName, InstanceValue> > associatedKeys = new Dictionary <Guid, IDictionary <XName, InstanceValue> >(); Dictionary <Guid, IDictionary <XName, InstanceValue> > completedKeys = new Dictionary <Guid, IDictionary <XName, InstanceValue> >(); foreach (Guid keyId in instance.RelatedKeys) { Key key = PersistenceItemManager.Load <Key>(keyId); if (key.TargetInstanceId == context.InstanceView.InstanceId) { if (key.State == InstanceKeyState.Completed) { completedKeys.Add(key.Id, ExcludeWriteOnlyPropertyBagItems(key.Metadata)); } else { associatedKeys.Add(key.Id, ExcludeWriteOnlyPropertyBagItems(key.Metadata)); } } } instance.State = instance.Data == null ? InstanceState.Uninitialized : InstanceState.Initialized; PersistenceItemManager.SaveToFile <Instance>(instance); context.LoadedInstance(instance.State, ExcludeWriteOnlyPropertyBagItems(instance.Data), ExcludeWriteOnlyPropertyBagItems(instance.Metadata), associatedKeys, completedKeys); return(0); }
private long ProcessSaveCommand(InstancePersistenceContext context, SaveWorkflowCommand command) { Owner owner = CheckOwner(context, command.Name); Instance instance = PersistenceItemManager.Load <Instance>(context.InstanceView.InstanceId); if (instance == null) { // Checking instance.Owner is like an InstanceLockQueryResult. context.QueriedInstanceStore(new InstanceLockQueryResult(context.InstanceView.InstanceId, Guid.Empty)); if (context.InstanceView.IsBoundToLock) { context.InstanceHandle.Free(); throw new InstanceLockLostException(command.Name, context.InstanceView.InstanceId); } instance = new Instance() { Version = 1, Id = context.InstanceView.InstanceId, Owner = context.InstanceView.InstanceOwner.InstanceOwnerId, Metadata = new PropertyBag() }; PersistenceItemManager.SaveToFile <Instance>(instance); context.BindAcquiredLock(1); } else { // Checking instance.Owner is like an InstanceLockQueryResult. context.QueriedInstanceStore(new InstanceLockQueryResult(context.InstanceView.InstanceId, instance.Owner)); if (instance.State == InstanceState.Completed) { throw new InstanceCompleteException(command.Name, context.InstanceView.InstanceId); } if (instance.Owner == Guid.Empty) { if (context.InstanceView.IsBoundToLock) { context.InstanceHandle.Free(); throw new InstanceLockLostException(command.Name, context.InstanceView.InstanceId); } instance.Version++; instance.Owner = context.InstanceView.InstanceOwner.InstanceOwnerId; PersistenceItemManager.SaveToFile <Instance>(instance); context.BindAcquiredLock(instance.Version); } else { if (instance.Owner != context.InstanceView.InstanceOwner.InstanceOwnerId) { if (context.InstanceView.IsBoundToLock) { context.InstanceHandle.Free(); throw new InstanceLockLostException(command.Name, context.InstanceView.InstanceId); } throw new InstanceLockedException(command.Name, instance.Owner); } if (context.InstanceView.IsBoundToLock) { if (context.InstanceVersion != instance.Version) { if (context.InstanceVersion > instance.Version) { throw new InvalidProgramException("This is a bug, the context should never be bound higher than the lock."); } context.InstanceHandle.Free(); throw new InstanceLockLostException(command.Name, context.InstanceView.InstanceId); } } else { // This is the very interesting parallel-convoy conflicting handle race resolution case. Two handles // can get bound to the same lock, which is necessary to allow parallel convoy to succeed without preventing // zombied locked instances from being reclaimed. return(instance.Version); } } } foreach (KeyValuePair <Guid, IDictionary <XName, InstanceValue> > keyEntry in command.InstanceKeysToAssociate) { Key key = PersistenceItemManager.Load <Key>(keyEntry.Key); if (key != null) { if (key.TargetInstanceId != Guid.Empty && key.TargetInstanceId != context.InstanceView.InstanceId) { throw new InstanceKeyCollisionException(command.Name, context.InstanceView.InstanceId, new InstanceKey(keyEntry.Key), key.TargetInstanceId); } // The SaveWorkflowCommand treats this as a no-op, whether completed or not. } else { key = new Key() { Id = keyEntry.Key, State = InstanceKeyState.Associated, TargetInstanceId = context.InstanceView.InstanceId, Metadata = new PropertyBag(keyEntry.Value) }; PersistenceItemManager.SaveToFile <Key>(key); context.AssociatedInstanceKey(keyEntry.Key); if (keyEntry.Value != null) { foreach (KeyValuePair <XName, InstanceValue> property in keyEntry.Value) { context.WroteInstanceKeyMetadataValue(keyEntry.Key, property.Key, property.Value); } } } } foreach (Guid keyGuid in command.InstanceKeysToComplete) { Key key = PersistenceItemManager.Load <Key>(keyGuid); if (key != null && key.TargetInstanceId == context.InstanceView.InstanceId) { if (key.State == InstanceKeyState.Associated) //if (key.State != InstanceKeyState.Completed) { key.State = InstanceKeyState.Completed; PersistenceItemManager.SaveToFile <Key>(key); context.CompletedInstanceKey(keyGuid); } } else { // The SaveWorkflowCommand does not allow this. (Should it validate against it?) throw new InvalidOperationException("Attempting to complete a key which is not associated."); } } foreach (Guid keyGuid in command.InstanceKeysToFree) { Key key = PersistenceItemManager.Load <Key>(keyGuid); if (key != null && key.TargetInstanceId == context.InstanceView.InstanceId) { if (key.State != InstanceKeyState.Completed) { context.CompletedInstanceKey(keyGuid); } key.State = InstanceKeyState.Unknown; key.TargetInstanceId = Guid.Empty; key.Metadata = null; PersistenceItemManager.SaveToFile <Key>(key); context.UnassociatedInstanceKey(keyGuid); } else { // The SaveWorkflowCommand does not allow this. (Should it validate against it?) throw new InvalidOperationException("Attempting to complete a key which is not associated."); } } foreach (KeyValuePair <Guid, IDictionary <XName, InstanceValue> > keyEntry in command.InstanceKeyMetadataChanges) { Key key = PersistenceItemManager.Load <Key>(keyEntry.Key); if (key != null && key.TargetInstanceId == context.InstanceView.InstanceId && key.State == InstanceKeyState.Associated) { if (keyEntry.Value != null) { foreach (KeyValuePair <XName, InstanceValue> property in keyEntry.Value) { if (property.Value.IsDeletedValue) { key.Metadata.Remove(property.Key); } else { key.Metadata[property.Key] = new InstanceValue(property.Value); } context.WroteInstanceKeyMetadataValue(keyEntry.Key, property.Key, property.Value); } PersistenceItemManager.SaveToFile <Key>(key); } } else { // The SaveWorkflowCommand does not allow this. (Should it validate against it?) throw new InvalidOperationException("Attempting to complete a key which is not associated."); } } foreach (KeyValuePair <XName, InstanceValue> property in command.InstanceMetadataChanges) { if (property.Value.IsDeletedValue) { instance.Metadata.Remove(property.Key); } else { instance.Metadata[property.Key] = new InstanceValue(property.Value); } context.WroteInstanceMetadataValue(property.Key, property.Value); } if (command.InstanceData.Count > 0) { instance.Data = new PropertyBag(command.InstanceData); context.PersistedInstance(command.InstanceData); } PersistenceItemManager.SaveToFile <Instance>(instance); // The command does the implicit advancement of everything into safe completed states. if (command.CompleteInstance) { if (instance.Data == null) { instance.Data = new PropertyBag(); PersistenceItemManager.SaveToFile <Instance>(instance); context.PersistedInstance(new Dictionary <XName, InstanceValue>()); } Queue <Guid> keysToComplete = new Queue <Guid>(); foreach (KeyValuePair <Guid, InstanceKeyView> keyEntry in context.InstanceView.InstanceKeys) { if (keyEntry.Value.InstanceKeyState == InstanceKeyState.Associated) { keysToComplete.Enqueue(keyEntry.Key); } } foreach (Guid keyToComplete in keysToComplete) { Key key = PersistenceItemManager.Load <Key>(keyToComplete); key.State = InstanceKeyState.Completed; PersistenceItemManager.SaveToFile <Key>(key); context.CompletedInstanceKey(keyToComplete); } instance.State = InstanceState.Completed; instance.Owner = Guid.Empty; PersistenceItemManager.SaveToFile <Instance>(instance); context.CompletedInstance(); context.InstanceHandle.Free(); } if (command.UnlockInstance) { instance.Owner = Guid.Empty; PersistenceItemManager.SaveToFile <Instance>(instance); context.InstanceHandle.Free(); } return(0); }
private long ProcessLoadByKeyCommand(InstancePersistenceContext context, LoadWorkflowByInstanceKeyCommand command) { Owner owner = CheckOwner(context, command.Name); Key key = PersistenceItemManager.Load <Key>(command.LookupInstanceKey); Instance instance; if (key == null) { if (context.InstanceView.IsBoundToLock && context.InstanceView.InstanceId != command.AssociateInstanceKeyToInstanceId) { // This happens in the bind reclaimed lock case. context.InstanceHandle.Free(); throw new InstanceLockLostException(command.Name, context.InstanceView.InstanceId); } if (command.AssociateInstanceKeyToInstanceId == Guid.Empty) { throw new InstanceKeyNotReadyException(command.Name, new InstanceKey(command.LookupInstanceKey)); } key = new Key() { Id = command.LookupInstanceKey, TargetInstanceId = command.AssociateInstanceKeyToInstanceId, Metadata = new PropertyBag() }; instance = PersistenceItemManager.Load <Instance>(command.AssociateInstanceKeyToInstanceId); if (instance == null) { // Checking instance.Owner is like an InstanceLockQueryResult. context.QueriedInstanceStore(new InstanceLockQueryResult(command.AssociateInstanceKeyToInstanceId, Guid.Empty)); if (context.InstanceView.IsBoundToLock) { // This happens in the bind reclaimed lock case. context.InstanceHandle.Free(); throw new InstanceLockLostException(command.Name, context.InstanceView.InstanceId); } context.BindInstance(command.AssociateInstanceKeyToInstanceId); if (command.AcceptUninitializedInstance) { instance = new Instance() { Id = command.AssociateInstanceKeyToInstanceId, Version = 1, Metadata = new PropertyBag(), Owner = context.InstanceView.InstanceOwner.InstanceOwnerId }; PersistenceItemManager.SaveToFile <Instance>(instance); } else { throw new Exception("Could not create new Instance"); } context.BindAcquiredLock(1); } else { // Checking instance.Owner is like an InstanceLockQueryResult. context.QueriedInstanceStore(new InstanceLockQueryResult(command.AssociateInstanceKeyToInstanceId, instance.Owner)); if (context.InstanceView.IsBoundToLock) { if (instance.Version != context.InstanceVersion || instance.Owner != context.InstanceView.InstanceOwner.InstanceOwnerId) { if (context.InstanceVersion > instance.Version) { throw new InvalidProgramException("This is a bug, the context should never be bound higher than the lock."); } context.InstanceHandle.Free(); throw new InstanceLockLostException(command.Name, context.InstanceView.InstanceId); } } if (instance.Data != null) { // LoadByInstanceKeyCommand only allows auto-association to an uninitialized instance. throw new InstanceCollisionException(command.Name, command.AssociateInstanceKeyToInstanceId); } if (!context.InstanceView.IsBoundToLock) { if (instance.Owner == Guid.Empty) { context.BindInstance(command.AssociateInstanceKeyToInstanceId); instance.Version++; instance.Owner = context.InstanceView.InstanceOwner.InstanceOwnerId; PersistenceItemManager.SaveToFile <Instance>(instance); context.BindAcquiredLock(instance.Version); } else if (instance.Owner == context.InstanceView.InstanceOwner.InstanceOwnerId) { // This is a pretty weird case - maybe it's a retry? context.BindInstance(command.AssociateInstanceKeyToInstanceId); return(instance.Version); } else { throw new InstanceLockedException(command.Name, instance.Owner); } } } if (command.InstanceKeysToAssociate.TryGetValue(command.LookupInstanceKey, out IDictionary <XName, InstanceValue> lookupKeyMetadata)) { key.Metadata = new PropertyBag(lookupKeyMetadata); } else { key.Metadata = new PropertyBag(); } key.Id = command.LookupInstanceKey; key.TargetInstanceId = command.AssociateInstanceKeyToInstanceId; PersistenceItemManager.SaveToFile <Key>(key); context.AssociatedInstanceKey(command.LookupInstanceKey); if (lookupKeyMetadata != null) { foreach (KeyValuePair <XName, InstanceValue> property in lookupKeyMetadata) { context.WroteInstanceKeyMetadataValue(command.LookupInstanceKey, property.Key, property.Value); } } } else { if (context.InstanceView.IsBoundToLock && (key.State == InstanceKeyState.Completed || key.TargetInstanceId != context.InstanceView.InstanceId)) { // This happens in the bind reclaimed lock case. context.InstanceHandle.Free(); throw new InstanceLockLostException(command.Name, context.InstanceView.InstanceId); } if (key.State == InstanceKeyState.Completed) { throw new InstanceKeyCompleteException(command.Name, new InstanceKey(command.LookupInstanceKey)); } instance = PersistenceItemManager.Load <Instance>(key.TargetInstanceId); // Checking instance.Owner is like an InstanceLockQueryResult. context.QueriedInstanceStore(new InstanceLockQueryResult(key.TargetInstanceId, instance.Owner)); if (context.InstanceView.IsBoundToLock) { if (instance.Version != context.InstanceVersion || instance.Owner != context.InstanceView.InstanceOwner.InstanceOwnerId) { if (context.InstanceVersion > instance.Version) { throw new InvalidProgramException("This is a bug, the context should never be bound higher than the lock."); } context.InstanceHandle.Free(); throw new InstanceLockLostException(command.Name, context.InstanceView.InstanceId); } } if (instance.Data == null && !command.AcceptUninitializedInstance) { throw new InstanceNotReadyException(command.Name, key.TargetInstanceId); } if (!context.InstanceView.IsBoundToLock) { if (instance.Owner == Guid.Empty) { context.BindInstance(key.TargetInstanceId); instance.Version++; instance.Owner = context.InstanceView.InstanceOwner.InstanceOwnerId; PersistenceItemManager.SaveToFile <Instance>(instance); context.BindAcquiredLock(instance.Version); } else if (instance.Owner == context.InstanceView.InstanceOwner.InstanceOwnerId) { // This is the very interesting parallel-convoy conflicting handle race resolution case. Two handles // can get bound to the same lock, which is necessary to allow parallel convoy to succeed without preventing // zombied locked instances from being reclaimed. context.BindInstance(key.TargetInstanceId); return(instance.Version); } else { throw new InstanceLockedException(command.Name, instance.Owner); } } } Key newKey; Exception exception = null; foreach (KeyValuePair <Guid, IDictionary <XName, InstanceValue> > keyEntry in command.InstanceKeysToAssociate) { newKey = PersistenceItemManager.Load <Key>(keyEntry.Key); if (newKey == null) { newKey = new Key() { Id = keyEntry.Key, TargetInstanceId = key.TargetInstanceId, Metadata = new PropertyBag(keyEntry.Value) }; PersistenceItemManager.AddKeyToInstance(key.TargetInstanceId, newKey); context.AssociatedInstanceKey(keyEntry.Key); if (keyEntry.Value != null) { foreach (KeyValuePair <XName, InstanceValue> property in keyEntry.Value) { context.WroteInstanceKeyMetadataValue(keyEntry.Key, property.Key, property.Value); } } } else { if (newKey.TargetInstanceId != key.TargetInstanceId && exception == null) { exception = new InstanceKeyCollisionException(command.Name, key.TargetInstanceId, new InstanceKey(keyEntry.Key), newKey.TargetInstanceId); } } } if (exception != null) { throw exception; } Dictionary <Guid, IDictionary <XName, InstanceValue> > associatedKeys = new Dictionary <Guid, IDictionary <XName, InstanceValue> >(); Dictionary <Guid, IDictionary <XName, InstanceValue> > completedKeys = new Dictionary <Guid, IDictionary <XName, InstanceValue> >(); foreach (Guid keyId in instance.RelatedKeys) { key = PersistenceItemManager.Load <Key>(keyId); if (key.TargetInstanceId == context.InstanceView.InstanceId) { if (key.State == InstanceKeyState.Completed) { completedKeys.Add(key.Id, ExcludeWriteOnlyPropertyBagItems(key.Metadata)); } else { associatedKeys.Add(key.Id, ExcludeWriteOnlyPropertyBagItems(key.Metadata)); } } } instance.State = instance.Data == null ? InstanceState.Uninitialized : InstanceState.Initialized; PersistenceItemManager.SaveToFile <Instance>(instance); context.LoadedInstance(instance.State, ExcludeWriteOnlyPropertyBagItems(instance.Data), ExcludeWriteOnlyPropertyBagItems(instance.Metadata), associatedKeys, completedKeys); return(0); }