///------------------------------------------------------------------------------------------------- /// <summary> /// Ajout d'une valeur. /// </summary> /// <exception cref="DuplicateElementException"> /// Thrown when a Duplicate Element error condition occurs. /// </exception> /// <param name="node"> /// . /// </param> /// <param name="ownerKey"> /// (Optional) /// </param> ///------------------------------------------------------------------------------------------------- public void AddNode(GraphNode node, Identity ownerKey = null) { DebugContract.Requires(node, "node"); using (var ctx = CreateCommandContext()) { var currentSlot = new Slot <GraphNode>(node); _valuesLock.EnterUpgradeableReadLock(); try { SlotList slots = null; if (!_values.TryGetValue(node.Id, out slots)) { // N'existe pas encore. On rajoute slots = new SlotList(node.Id, node.NodeType, ownerKey); _valuesLock.EnterWriteLock(); try { _values.Add(node.Id, slots); } finally { _valuesLock.ExitWriteLock(); } AddSlot(ctx, slots, currentSlot); // No vaccum notification here since the element is new } else { if (SelectSlot(node.Id, ctx) != null) { throw new DuplicateElementException(node.Id.ToString()); } if (_values.TryGetValue(node.Id, out slots)) { var initialSlot = slots.GetActiveSlot(); AddSlot(ctx, slots, currentSlot); if (initialSlot != null) { initialSlot.CMin = ctx.CommandId; initialSlot.XMax = ctx.Transaction.Id; } NotifyVacuum(slots); } } _trace.WriteTrace(TraceCategory.MemoryStore, "Add {0} - {1}", node.Id, node); ctx.Complete(); } finally { _statAddValue.Incr(); _valuesLock.ExitUpgradeableReadLock(); } } }
private void NotifyDiagnosticMessages(bool disposing, SessionMode currentMode, IEnumerable <IEventNotifier> notifiers, Tuple <ExecutionResult, ISessionInformation> result) { var messages = result.Item1; try { if (result.Item2 != null) // Finalize { // Enfin notification des erreurs // Cette partie ne doit pas planter toute erreur sera ignorée using (CodeMarker.MarkBlock("Session.Notifications")) { foreach (var notifier in notifiers) { notifier.NotifyMessages(result.Item2, messages); } } } } catch (Exception ex) { messages.AddMessage(new DiagnosticMessage(MessageType.Error, ex.Message, "Session", SessionDataContext.InValidationProcess, null, ex)); _trace.WriteTrace(TraceCategory.Session, ExceptionMessages.Trace_FatalErrorInEventsNotificationProcessFormat, ex.Message); } finally { if (disposing && messages.ShouldRaiseException() && (currentMode & SessionMode.SilentMode) != SessionMode.SilentMode) { throw new SessionException(messages.Messages); } } }
///------------------------------------------------------------------------------------------------- /// <summary> /// Adds the element. /// </summary> /// <param name="id"> /// . /// </param> /// <param name="schemaEntity"> /// The meta class. /// </param> /// <returns> /// The new entity. /// </returns> ///------------------------------------------------------------------------------------------------- public virtual GraphNode CreateEntity(Identity id, ISchemaEntity schemaEntity) { DebugContract.Requires(id, "id"); DebugContract.Requires(schemaEntity); if (Session.Current == null) { throw new SessionRequiredException(); } Session.Current.AcquireLock(LockType.Exclusive, id); _trace.WriteTrace(TraceCategory.Hypergraph, "Add element {0}", id); using (var tx = BeginTransaction()) { if (CurrentTransaction != null) { CurrentTransaction.UpdateProfiler(p => p.NodesCreated.Incr()); CurrentTransaction.UpdateProfiler(p => p.NumberOfNodes.Incr()); } var node = new GraphNode(id, schemaEntity.Id, NodeType.Node); _storage.AddNode(node); tx.Commit(); return(node); } }
///------------------------------------------------------------------------------------------------- /// <summary> /// Start a new transaction. /// </summary> /// <param name="isolationLevel"> /// . /// </param> /// <param name="readOnly"> /// (Optional) true to read only. /// </param> /// <returns> /// A MemoryTransaction. /// </returns> ///------------------------------------------------------------------------------------------------- public MemoryTransaction BeginTransaction(SessionIsolationLevel isolationLevel, bool readOnly = false) { if (isolationLevel == SessionIsolationLevel.Unspecified) { var s = Session.Current; isolationLevel = s != null ? Session.Current.SessionIsolationLevel : SessionIsolationLevel.ReadCommitted; } var tx = CreateTransaction(isolationLevel, readOnly); if (isolationLevel == SessionIsolationLevel.Serializable) { var list = GetActiveTransactions(); tx.ActiveTransactionsWhenStarted = list; if (_trace.IsEnabled(TraceCategory.MemoryStore)) { _trace.WriteTrace(TraceCategory.MemoryStore, "Active transaction when tx {0} starts : {1}", tx.Id, list != null ? String.Join(",", list.Select(t => t.Id).ToArray()) : ""); } } return(tx); }
///------------------------------------------------------------------------------------------------- /// <summary> /// Acquires the lock. /// </summary> /// <exception cref="DeadLockException"> /// Thrown when a Dead Lock error condition occurs. /// </exception> /// <exception cref="SerializableTransactionException"> /// Thrown when a Serializable Transaction error condition occurs. /// </exception> /// <param name="session"> /// The session. /// </param> /// <param name="key"> /// The key. /// </param> /// <param name="mode"> /// The mode. /// </param> /// <returns> /// An IDisposable. /// </returns> ///------------------------------------------------------------------------------------------------- public IDisposable AcquireLock(ISession session, object key, LockType mode) { Contract.Requires(key, "key"); Contract.Requires(session, "session"); //if (session.IsDisposing) //{ // throw new Exception("Impossible d'acquerir un lock sur une session en train de se terminer. Si vous avez lancé des traitements en parralléles pendant la session, assurez vous qu'ils soient terminé quand la session est disposé."); //} LockInfo info; var deadlockTimeLimit = DeadLockLimitTimeInMs; // Un lock est tenu par la transaction englobante. // Il sera libéré quand celle ci se terminera var txId = session.SessionId; var resource = key.ToString(); _trace.WriteTrace(TraceCategory.LockManager, "Try to acquire lock for {0} in tx {1}", resource, txId); // On va essayer d'obtenir un lock virtuel pour la ressource demandée // Un lock virtuel est une structure qui contient le lock physique et le map entre la transaction qui a obtenu le lock et la ressource concernée _sync.EnterWriteLock(); try { // Etat du lock virtuel avant d'essayer d'avoir le lock physique if (!_locks.TryGetValue(resource, out info)) { // Ce lock n'a jamais été traité, on le crée info = new LockInfo { SessionId = txId, Ressource = resource, Lock = new ReaderWriterLockSlim(), SessionStatus = TransactionStatus.Active, Mode = mode }; // Nouveau lock, on peut obtenir tout de suite le lock physique if ((mode & LockType.Exclusive) == LockType.Exclusive) { info.Lock.EnterWriteLock(); } else { info.Lock.EnterUpgradeableReadLock(); } _locks[resource] = info; session.Locks.Add(info); info.AddRef(); _trace.WriteTrace(TraceCategory.LockManager, "Lock acquired for {0} in tx {1}", resource, txId); return(new LockWrapper(this, info)); } // Ce lock existe dèjà. // Pas grave si c'est dans le même thread if (session.SessionId == info.SessionId) { if (info.Mode != mode) { if (info.Mode == LockType.Shared) { info.Lock.EnterWriteLock(); info.Mode = mode; info.Promoted = true; } } _trace.WriteTrace(TraceCategory.LockManager, "Use lock from parent transaction for {0} in tx {1}", resource, txId); return(new LockWrapper()); // Il n'est pas possible de libèrer ce lock ici } info.AddRef(); _trace.WriteTrace(TraceCategory.LockManager, "Waiting lock for {0} in tx {1}", resource, txId); } finally { _sync.ExitWriteLock(); } // Le lock est dèjà pris. // On va essayer de l'obtenir mais avec un time out (pour pouvoir traiter le dead lock) if (((LockType.Exclusive & mode) == LockType.Exclusive ? !info.Lock.TryEnterWriteLock(deadlockTimeLimit) : !info.Lock.TryEnterUpgradeableReadLock(deadlockTimeLimit))) { _trace.WriteTrace(TraceCategory.LockManager, "Deadlock for {0} in tx {1}", resource, txId); if (info.DecRef() == 0) { _sync.EnterWriteLock(); try { if (info.DecRef() == 0) { _locks.Remove(resource); } } finally { _sync.ExitWriteLock(); } } // On ne l'a pas throw new DeadLockException(); // c'est un dead lock, on abandonne } _trace.WriteTrace(TraceCategory.LockManager, "Transaction {0} has released lock for {1}", info.SessionId, resource); // On vient d'avoir le lock // Si on est pas dans le scope de la transaction qui tenait le lock et que le niveau de sérialisation est Serialize alors // Si la transaction qui tenait le lock s'est correctement terminé il y a conflit if (session.SessionIsolationLevel == SessionIsolationLevel.Serializable && session.SessionId != info.SessionId) { // Est ce que la transaction concurrente s'est bien terminée ? // Si concurrentTransaction est null, c'est que la transaction référencée s'est bien terminée et qu'elle a été purgé par le vaccum if (info.SessionStatus == TransactionStatus.Committed) { // Si on est en mode exclusive, on va générer une erreur if ((mode & LockType.Exclusive) == LockType.Exclusive) { if ((mode & LockType.ExclusiveWait) != LockType.ExclusiveWait) { _trace.WriteTrace(TraceCategory.LockManager, "Serialize transaction error for {0}", resource); _sync.EnterWriteLock(); try { if (info.DecRef() == 0) { _locks.Remove(resource); } } finally { _sync.ExitWriteLock(); } info.Lock.ExitWriteLock(); throw new SerializableTransactionException(resource); } } } } // Sinon tout est OK _trace.WriteTrace(TraceCategory.LockManager, "Lock acquired (after waiting) for {0} in tx {1}", resource, txId); info.SessionId = txId; info.SessionStatus = TransactionStatus.Active; info.Mode = mode; _sync.EnterWriteLock(); try { session.Locks.Add(info); } finally { _sync.ExitWriteLock(); } return(new LockWrapper(this, info)); }