📃
Tech White Papers
  • 📃White Papers
  • 🪶Apache
    • Kafka (EN)
      • Kafka Connect
      • Kafka Streams
      • ksqlDB
    • Ignite (TR)
      • Clustering
        • Baseline Topology
      • Thin Clients
      • Data Modeling
        • Data Partitioning
        • Affinity Colocation
      • Memory Architecture
      • Persistence
        • External Storage
        • Swapping
        • Snapshot
        • Disk Compression
        • Persistence Tuning
        • Change Data Capture
      • Cluster Snapshots
      • Data Rebalancing
      • Data Streaming
      • Using Key-Value API
        • Basic Cache Operations
        • Working With Binary Objects
      • Performing Transactions
      • Working with SQL
        • Understanding Schemas
        • Defining Indexes
        • Distributed Joins
      • Distributed Computing
      • Machine Learning
      • Using Continuous Queries
      • Using Ignite Messaging
      • .NET Specific
        • LINQ
        • Serialization
      • Working With Events
        • Events
      • Performance and Troubleshooting
        • Generic Performance Tips
        • Memory and JVM Tuning
        • Persistence Tuning
        • SQL Performance Tuning
        • Thread Pools Tuning
    • Pulsar (TR)
  • 📜Data
    • ClickHouse (TR)
    • QuestDB (TR)
  • Comparison
    • Pulsar vs Kafka
    • ClickHouse vs QuestDB
  • Architectural
    • Microservices
      • Design Principles
      • Design Patterns
Powered by GitBook
On this page
  • Executing Transactions
  • Concurrency Modes and Isolation Levels
  • Deadlock Detection
  • Deadlock-free Transactions

Was this helpful?

  1. Apache
  2. Ignite (TR)

Performing Transactions

03/02/2023

PreviousWorking With Binary ObjectsNextWorking with SQL

Last updated 2 years ago

Was this helpful?

Belirli bir cache için transaction desteği etkinleştirilmesi için, cache yapılandırmasında atomicityMode parametresi TRANSACTIONAL olarak ayarlanabilir. Detaylı bilgi için bölümüne bakabilirsiniz.

Transactionlar, bir veya daha fazla key’deki birden çok cache işlemini tek bir atomik işlemde gruplandırmaya olanak tanır. Bu işlemler, belirtilen keylerde başka serpiştirilmiş işlemler olmadan yürütülür ve ya tümü başarılı olur ya da tümü başarısız olur. İşlemlerin parçalı olarak yürütülmesi söz konusu değildir.

Cache yapılandırmasında belirli bir önbellek için transactionlar etkinleştirilebilir;

⌨️ XML Config
<bean class="org.apache.ignite.configuration.IgniteConfiguration">

    <property name="transactionConfiguration">
        <bean class="org.apache.ignite.configuration.TransactionConfiguration">
            <!--Set the timeout to 20 seconds-->
            <property name="TxTimeoutOnPartitionMapExchange" value="20000"/>
        </bean>
    </property>

</bean>
⌨️ .NET Config
var cfg = new IgniteConfiguration
{
    CacheConfiguration = new[]
    {
        new CacheConfiguration("txCache")
        {
            AtomicityMode = CacheAtomicityMode.Transactional
        }
    },
    TransactionConfiguration = new TransactionConfiguration
    {
        DefaultTransactionConcurrency = TransactionConcurrency.Optimistic
    }
};

Executing Transactions

Key/Value API'si, transactionları başlatma ve tamamlamanın yanı sıra transaction ile ilgili metrikleri almak için bir interface sağlar. Interface, bir Ignite nesnesinden elde edilebilir.

⌨️ .NET Sample
var cfg = new IgniteConfiguration
{
    DiscoverySpi = new TcpDiscoverySpi
    {
        LocalPort = 48500,
        LocalPortRange = 20,
        IpFinder = new TcpDiscoveryStaticIpFinder
        {
            Endpoints = new[]
            {
                "127.0.0.1:48500..48520"
            }
        }
    },
    CacheConfiguration = new[]
    {
        new CacheConfiguration
        {
            Name = "cacheName",
            AtomicityMode = CacheAtomicityMode.Transactional
        }
    },
    TransactionConfiguration = new TransactionConfiguration
    {
        DefaultTimeoutOnPartitionMapExchange = TimeSpan.FromSeconds(20)
    }
};

var ignite = Ignition.Start(cfg);
var cache = ignite.GetCache<string, int>("cacheName");
cache.Put("Hello", 1);
var transactions = ignite.GetTransactions();

using (var tx = transactions.TxStart())
{
    int hello = cache.Get("Hello");

    if (hello == 1)
    {
        cache.Put("Hello", 11);
    }

    cache.Put("World", 22);

    tx.Commit();
}

Concurrency Modes and Isolation Levels

TRANSACTIONAL atomicity moduna sahip cacheler, transactionlar için hem OPTIMISTIC hem de PESSIMISTIC eşzamanlılık(concurrency) modlarını destekler. Eşzamanlılık modu, giriş düzeyinde bir transaction kilidinin ne zaman alınacağını belirler: veri erişimi sırasında veya hazırlık aşamasında. Kilitleme, bir nesneye eşzamanlı erişimi engeller. Örneğin, pessimistic kilitleme ile bir ToDo list öğesini güncellemeye çalıştığınızda, siz transaction’u commitleyene veya rollback yapana kadar sunucu nesneye bir kilit yerleştirir ve başka hiçbir işlem veya işlemin aynı girişi güncellemesine izin verilmez. Bir transactionda kullanılan eşzamanlılık modundan bağımsız olarak, transactionda listelenen tüm girişlerin committen önce kilitlendiği bir an vardır.

Izolasyon(isolation) düzeyi, eşzamanlı transactionların aynı keyler üzerindeki işlemleri nasıl 'gördüğünü' ve ele aldığını tanımlar. Ignite, READ_COMMITTED, REPEATABLE_READ ve SERIALIZABLE izolasyon seviyelerini destekler.

Eşzamanlılık modlarının ve izolasyon düzeylerinin tüm kombinasyonlarına izin verilir. Aşağıda, sistem davranışının açıklaması ve her bir eşzamanlılık-izolasyon kombinasyonu tarafından sağlanan garantiler yer almaktadır.

Pessimistic Transactions

PESSIMISTIC transactionlarda kilitler, ilk okuma veya yazma erişimi sırasında (izolasyon seviyesine bağlı olarak) alınır ve commit edilene veya rollback olana kadar transaction tarafından tutulur. Bu modda, kilitler önce birincil node’larda alınır ve ardından hazırlık aşaması sırasında yedek node’lara yükseltilir. Aşağıdaki izolasyon seviyeleri, PESSIMISTIC eşzamanlılık modu ile yapılandırılabilir:

  • READ_COMMITTED → Veriler kilitlenmeden okunur ve hiçbir zaman transaction’ın kendisinde cache’e alınmaz. Cache yapılandırmasında buna izin veriliyorsa, veriler bir yedekleme node’undan okunabilir. Bu izolasyon modunda, Tekrarlanamayan Okumalara sahip olabilirsiniz çünkü eşzamanlı bir transaction, transactionunuzda verileri iki kez okurken verileri değiştirebilir. Kilit, yalnızca ilk yazma erişimi sırasında alınır (bu, EntryProcessor çağrısını içerir). Bu, transaction sırasında okunan bir girişin, transaction’ın gerçekleştirildiği zamana kadar farklı bir değere sahip olabileceği anlamına gelir. Bu durumda herhangi bir hata oluşmaz.

  • REPEATABLE_READ → Giriş kilidi alınır ve veriler ilk okuma veya yazma erişimindeki birincil node’dan alınır ve yerel transaction haritasında(local transaction map) depolanır. Aynı verilere ardışık tüm erişimler yereldir ve son okunan veya güncellenen transaction değerini döndürür. Bu, başka hiçbir eşzamanlı transaction’ın kilitli verilerde değişiklik yapamayacağı ve transactionınız için Tekrarlanabilir Okumalar alacağınız anlamına gelir.

  • SERIALIZABLE → PESSIMISTIC modunda, bu izolasyon seviyesi REPEATABLE_READ ile aynı şekilde çalışır.

PESSIMISTIC modunda kilitleme sırasının önemli olduğunu unutmayın. Ayrıca kilitler sırayla ve tam olarak belirtilen sırada alınır.

En az bir PESSIMISTIC transaction kilidi elde edilirse, transaction commitlenene veya rollback olana kadar cache topolojisini değiştirmenin imkansız olduğunu unutmayın. Bu nedenle, transaction kilitlerini uzun süre tutmaktan kaçınmalısınız.

Optimistic Transactions

OPTIMISTIC transactionlarda, 2PC'nin ilk aşamasında, hazırlık adımında birincil node’larda giriş kilitleri alınır ve ardından yedek nodelara yükseltilir ve transaction tamamlandıktan sonra serbest bırakılır. Transaction’ı geri alırsanız ve hiçbir commit girişiminde bulunulmazsa kilitler asla alınmaz. OPTIMISTIC eşzamanlılık modu ile aşağıdaki izolasyon seviyeleri yapılandırılabilir:

READ_COMMITTED → Cache’e uygulanması gereken değişiklikler kaynak node’da toplanır ve transaction commitinde uygulanır. Transaction verileri kilitlenmeden okunur ve transactionda asla cache’e alınmaz. Cache yapılandırmasında buna izin veriliyorsa, veriler bir yedekleme node’undan okunabilir. Bu izolasyonda, Tekrarlanamayan Okumalara sahip olabilirsiniz çünkü eşzamanlı bir transaction, transactionınınızda verileri iki kez okurken verileri değiştirebilir. Bu mod kombinasyonu, giriş değerinin ilk okuma veya yazma erişiminden bu yana değiştirilip değiştirilmediğini kontrol etmez ve asla optimistic bir hata oluşturmaz.

REPEATABLE_READ → Bu yalıtım düzeyindeki transactionlar, tek bir farkla OPTIMISTIC READ_COMMITTED işlemlerine benzer şekilde çalışır: okuma değerleri kaynak node’da cache’e alınır ve sonraki tüm okumaların yerel olması garanti edilir. Bu mod kombinasyonu, giriş değerinin ilk okuma veya yazma erişiminden bu yana değiştirilip değiştirilmediğini kontrol etmez ve asla optimistic bir hata oluşturmaz.

SERIALIZABLE → İlk okuma erişimi üzerine bir giriş sürümünü saklar. Ignite motoru, başlatılan transaction’ın bir parçası olarak kullanılan girdilerden en az birinin değiştirildiğini algılarsa, Ignite işlemi tamamlama aşamasında başarısız olur. Kısacası, bu, Ignite'ın bir transaction’ın commit aşamasında bir çakışma olduğunu tespit etmesi durumunda, transaction’ı başarısız kılarak TransactionOptimisticException'ı atması ve yapılan değişiklikleri geri alması anlamına gelir. Bu hatayı handle ettiğinizden emin olun ve transaction’ı yeniden deneyin.

⌨️ .NET Sample
var cfg = new IgniteConfiguration
{
    DiscoverySpi = new TcpDiscoverySpi
    {
        LocalPort = 48500,
        LocalPortRange = 20,
        IpFinder = new TcpDiscoveryStaticIpFinder
        {
            Endpoints = new[]
            {
                "127.0.0.1:48500..48520"
            }
        }
    },
    CacheConfiguration = new[]
    {
        new CacheConfiguration
        {
            Name = "cacheName",
            AtomicityMode = CacheAtomicityMode.Transactional
        }
    },
    TransactionConfiguration = new TransactionConfiguration
    {
        DefaultTimeoutOnPartitionMapExchange = TimeSpan.FromSeconds(20)
    }
};

var ignite = Ignition.Start(cfg);
// Re-try the transaction a limited number of times
var retryCount = 10;
var retries = 0;

// Start a transaction in the optimistic mode with the serializable isolation level
while (retries < retryCount)
{
    retries++;
    try
    {
        using (var tx = ignite.GetTransactions().TxStart(TransactionConcurrency.Optimistic,
            TransactionIsolation.Serializable))
        {
            // modify cache entries as part of this transaction.

            // commit the transaction
            tx.Commit();

            // the transaction succeeded. Leave the while loop.
            break;
        }
    }
    catch (TransactionOptimisticException)
    {
        // Transaction has failed. Retry.
    }

}

Burada dikkat edilmesi gereken bir diğer önemli nokta, bir giriş değiştirilmeden okunsa bile (cache.put(…)) bir işlemin başarısız olmasıdır, çünkü girişin değeri başlatılan transaction içindeki mantık için önemli olabilir.

READ_COMMITTED ve REPEATABLE_READ transactionları için key sırasının önemli olduğunu unutmayın, çünkü bu modlarda kilitler yine sıralı olarak alınır.

Read Consistency

PESSIMISTIC modunda tam okuma tutarlılığı elde etmek için okuma kilitlerinin edinilmesi gerekir. Bu, PESSIMISTIC modundaki okumalar arasındaki tam tutarlılığın yalnızca PESSIMISTIC REPEATABLE_READ (veya SERIALIZABLE) transactionlarıyla sağlanabileceği anlamına gelir.

OPTIMISTIC transactionları kullanılırken, okumalar arasındaki olası çakışmalara izin verilmeyerek tam okuma tutarlılığı sağlanabilir. Bu davranış OPTIMISTIC SERIALIZABLE modu tarafından sağlanır. Ancak, commit gerçekleşene kadar parçalı bir transaction durumunu hala okuyabileceğinizi, bu nedenle transaction mantığının buna karşı koruma sağlaması gerektiğini unutmayın. Yalnızca commit aşamasında, herhangi bir çakışma durumunda, transaction’ı yeniden denemenizi sağlayan bir TransactionOptimisticException atılır.

PESSIMISTIC REPEATABLE_READ veya SERIALIZABLE transactionları veya OPTIMISTIC SERIALIZABLE transactionları kullanmıyorsanız parçalı transaction durumu görmeniz mümkündür. Bu, bir transaction A ve B nesnelerini güncellerse, başka bir transaction’un A için yeni değeri ve B için eski değeri görebileceği anlamına gelir.

Deadlock Detection

Dağıtık transactionlarda çalışırken bilmeniz gereken önemli bir kural, bir transactiona katılan keylerin kilitlerinin aynı sırada alınması gerektiğidir. Bu kuralı ihlal etmek, dağıtık bir deadlock’a yol açabilir.

Ignite, dağıtık deadlocklardan kaçınmaz, bunun yerine bu tür durumlarda hata ayıklamayı ve düzeltmeyi kolaylaştıran built-in işlevselliğe sahiptir.

Aşağıdaki kod parçacığında, zaman aşımı ile bir transaction başlatıldı. Zaman aşımı sona ererse, deadlock algılama prosedürü, zaman aşımına neden olabilecek olası bir deadlock bulmaya çalışır. Zaman aşımı süresi dolduğunda, deadlocktan bağımsız olarak CacheException'ın nedeni olarak TransactionTimeoutException oluşturulur ve fırlatılır. Bununla birlikte, bir deadlock algılanırsa, döndürülen TransactionTimeoutException'ın nedeni TransactionDeadlockException olacaktır (deadlock’a dahil olan en az bir transaction için).

⌨️ .NET Sample
var cfg = new IgniteConfiguration
{
    DiscoverySpi = new TcpDiscoverySpi
    {
        LocalPort = 48500,
        LocalPortRange = 20,
        IpFinder = new TcpDiscoveryStaticIpFinder
        {
            Endpoints = new[]
            {
                "127.0.0.1:48500..48520"
            }
        }
    },
    CacheConfiguration = new[]
    {
        new CacheConfiguration
        {
            Name = "cacheName",
            AtomicityMode = CacheAtomicityMode.Transactional
        }
    },
    TransactionConfiguration = new TransactionConfiguration
    {
        DefaultTimeoutOnPartitionMapExchange = TimeSpan.FromSeconds(20)
    }
};

var ignite = Ignition.Start(cfg);
var intCache = ignite.GetOrCreateCache<int, int>("intCache");
try
{
    using (var tx = ignite.GetTransactions().TxStart(TransactionConcurrency.Pessimistic,
        TransactionIsolation.ReadCommitted, TimeSpan.FromMilliseconds(300), 0))
    {
        intCache.Put(1, 1);
        intCache.Put(2, 1);
        tx.Commit();
    }
}
catch (TransactionTimeoutException e)
{
    Console.WriteLine(e.Message);
}
catch (TransactionDeadlockException e)
{
    Console.WriteLine(e.Message);
}

TransactionDeadlockException mesajı, deadlock’un nedenini bulmanıza yardımcı olabilecek yararlı bilgiler içerir.

Deadlock detected:

K1: TX1 holds lock, TX2 waits lock.
K2: TX2 holds lock, TX1 waits lock.

Transactions:

TX1 [txId=GridCacheVersion [topVer=74949328, time=1463469328421, order=1463469326211, nodeOrder=1], nodeId=ad68354d-07b8-4be5-85bb-f5f2362fbb88, threadId=73]
TX2 [txId=GridCacheVersion [topVer=74949328, time=1463469328421, order=1463469326210, nodeOrder=1], nodeId=ad68354d-07b8-4be5-85bb-f5f2362fbb88, threadId=74]

Keys:

K1 [key=1, cache=default]
K2 [key=2, cache=default]

Deadlock tespiti, clusterdaki nodeların sayısına, keylere ve olası bir deadlock’a dahil olan transactionlara bağlı olarak birçok yineleme alabilen çok adımlı bir prosedürdür. Deadlock algılama başlatıcısı, bir transaction’un başlatıldığı ve bir TransactionTimeoutException ile başarısız olduğu bir nodedur. Bu node, diğer remote nodelarla reques/response alışverişi yaparak bir deadlock olup olmadığını araştırır ve ardından TransactionDeadlockException ile sağlanan deadlock ile ilgili bir rapor hazırlar. Bu tür her mesaj (request/response) yineleme olarak bilinir.

Bir transaction, deadlock algılama prosedürü tamamlanana kadar geri alınmadığından(rollback), bir transaction’un geri alınması için öngörülebilir bir süre istiyorsanız, bazen parametreleri ayarlamak mantıklıdır (aşağıda gösterilmektedir).

  • IgniteSystemProperties.IGNITE_TX_DEADLOCK_DETECTION_MAX_ITERS → Deadlock algılama prosedürü için maksimum yineleme sayısını belirtir. Bu özelliğin değeri sıfırdan küçük veya sıfıra eşitse, deadlock tespiti devre dışı bırakılır (varsayılan olarak 1000);

  • IgniteSystemProperties.IGNITE_TX_DEADLOCK_DETECTION_TIMEOUT → Deadlock algılama mekanizması için zaman aşımını belirtir (varsayılan olarak 1 dakika).

Çok az yineleme varsa, tamamlanmamış bir deadlock raporu alabileceğinizi unutmayın.

Deadlock-free Transactions

OPTIMISTIC SERIALIZABLE transactionlar için kilitler sıralı olarak alınmaz. Bu modda, keylere herhangi bir sırada erişilebilir çünkü transaction kilitleri, Ignite'ın deadlocklarından kaçınmasına izin veren ek bir kontrolle paralel olarak alınır.

SERIALIZABLE transactionlarda kilitlerin nasıl çalıştığını açıklamak için bazı kavramları bilmek gerekiyor. Ignite'ta her işleme, XidVersion adlı karşılaştırılabilir bir sürüm atanır. Transaction commit’i üzerine, transactionda yazılan her girdiye EntryVersion adı verilen yeni bir karşılaştırılabilir sürüm atanır. XidVersionA sürümüne sahip bir OPTIMISTIC SERIALIZABLE transaction’u, aşağıdaki durumlarda TransactionOptimisticException ile başarısız olur:

  • SERIALIZABLE transactionunun bir girişinde kilit tutan, devam eden bir PESSIMISTIC veya non-serializable OPTIMISTIC transaction var.

  • XidVersionB > XidVersionA şeklinde XidVersionB sürümüyle devam eden başka bir OPTIMISTIC SERIALIZABLE transaction var ve bu transaction SERIALIZABLE transactionunun bir girişinde kilit tutuyor.

  • OPTIMISTIC SERIALIZABLE transaction’u gerekli tüm kilitleri edindiğinde, geçerli sürüm, committen önce gözlemlenen sürümden farklı olan bir giriş vardır.

Yüksek derecede eşzamanlı bir ortamda, optimistic kilitleme, yüksek transaction başarısızlık oranına yol açabilir, ancak pessimistic kilitleme, kilitler transactionlar tarafından farklı bir sırayla elde edilirse deadlocklara yol açabilir.

Bununla birlikte, contention-free bir ortamda, optimistic serializable kilitleme, büyük transactionlar için daha iyi performans sağlayabilir çünkü network trip sayısı yalnızca transaction’un yayıldığı node sayısına bağlıdır ve transactiondaki key sayısına bağlı değildir.

🪶
Atomicity Modes
Daha fazla bilgi için…