Starcounter
HomeDownloadDocsCommunity
2.3.1
2.3.1
  • Starcounter Documentation
  • Getting Started
  • Starcounter
    • Collapsing the Stack
      • Complexity and Scalability Tradeoff
      • The Future of Micro-Services
      • 10 Benefits of Collapsing the Stack
    • Integrated Database and Web Server
  • Hello World - Tutorial
    • Create a Database Class
    • Create a Real Time UI
    • First Interactive UI
    • Computed Properties
    • Expense Tracker
    • Cancel and Delete
    • The Next Step
  • Guides
    • Database
      • Database Classes
      • Data manipulation
      • Object Identity and Object References
      • Querying with SQL
      • Data Types
      • Relations
      • Inheritance
      • Sharing data
      • Database Configuration
      • Comparing Database Objects
      • Referential Integrity and Constraints
    • SQL
      • Identifiers
      • Path Expressions
      • Data operators
      • Joins
      • Aggregates
      • Comparisons and Logical Operators
      • Sorting
      • Fetch
      • Offset Key
      • Indexes
      • Literals
      • Query Plan Hints
      • Reserved words
      • Query for Database Classes
      • SQL Isolation Between Applications
    • Transactions
      • Short-Running Transactions
      • Long running transactions
      • Using Transactions
      • Running Background Jobs
      • Commit Hooks
    • Typed JSON
      • JSON-by-example
      • Code-Behind
      • Data Bindings
      • Callback Methods
      • Responding with JSON
      • Accepting JSON in Requests
      • Primitive Arrays and Single Value Types
      • Typed JSON Internals
    • Blendable Web Apps
      • Starcounter MVVM
      • Palindrom
      • Client-Side Stack
      • Sessions
      • HTML Views
      • App Shell
      • Web Components
      • View Attaching
      • View Composing
      • HTML Compositions
      • HTML Views Blending Guidelines
      • Avoiding CSS conflicts
      • Debugging
    • Network
      • HTTP
      • Internal Self Calls
      • Middleware
      • Anonymous or Substitute Handlers
      • URL Aliases and Redirects
      • Network Gateway
      • Static File Server
      • External HTTP Calls
      • WebSocket
      • Avoiding URI conflicts
      • TCP Sockets
      • UDP Sockets
    • Publishing Apps
    • Working with Starcounter
      • Release Channels
      • Starting and Stopping Apps
      • Administrator Web UI
      • Star CLI
      • StarAdmin CLI
      • StarDump CLI
      • Working in Visual Studio
      • Error Log
      • Using HTTPS on NGINX
      • Using HTTPS on IIS
      • Run Starcounter in Production
      • Weaver
      • Investigating App Crashes
      • Configuration Structure
      • Database Refactoring
      • Using Unload/Reload to Modify Database Schema
      • Kernel Questions and Answers
      • Log Files
  • Cookbook
    • Attach an HTTP Request to an Existing Long-Running Transaction
    • Cookie-Based Authentication
    • Timestamp on Object Creation
    • Creating Strongly Typed JSON Collections
    • Migrating From 2.2 to 2.3+
    • Multiple Pages
    • Icons
    • Proposed Project Structure
    • Acceptance Testing with Selenium
    • Requesting a User to Authenticate
    • How to delete unused tables and columns
Powered by GitBook
On this page
  • Introduction
  • Db.Transact
  • Db.TransactAsync
  • Limitations
  • Transaction Size
  1. Guides
  2. Transactions

Short-Running Transactions

PreviousTransactionsNextLong running transactions

Last updated 7 years ago

Introduction

Short-running transaction creates a transaction that commits to the database when the scope runs out. These transactions are ACID. There are two types of short-running transactions - synchronous and asynchronous.

Db.Transact

Db.Transact is the simplest way to create a transaction in Starcounter. It declares a transactional scope and runs synchronously, as described on the . The argument passed to the Db.Transact method is a delegate containing the code to run within the transaction. In code, it looks like this:

Db.Transact(() =>
{
    new Employee
    {
        FirstName = "Samwise",
        LastName = "Gamgee"
    };
});

Since Db.Transact is synchronous, it sometimes becomes a performance bottleneck. Starcounter handles this by automatically scaling the number of working threads to continue processing requests even if some handlers are blocked. The maximum number of working threads is the number of CPU cores multiplied by 254, so with four cores, there would be a maximum of 1016 working threads. When these threads are occupied, the next Db.Transact call in line will have to wait until a thread is freed. circumvents this.

Db.Transact is an implementation of Db.TransactAsync with a thin wrapper that synchronously waits for the returned Task object.

Db.TransactAsync

Db.TransactAsync is the asynchronous counterpart of Db.Transact. It gives the developer more control to balance throughput and latency. The function returns a Task that is marked as completed when flushing the transaction log for the transaction. Thus, the database operation itself is synchronous while flushing to the transaction log is asynchronous.

Db.Transact and Db.TransactAsync are syntactically identical:

Db.TransactAsync(() => 
{
    // The code to run in the transaction
})

While waiting for the write to the transaction log to finish, it's possible to do other things, such as sending an email:

Order order = null;
Task task = Db.TransactAsync(() =>
{
    order = new Order();
}); // Order has been added to the database

SendConfirmationEmail(order);

task.Wait(); // Wait for write to log to finish

This is more flexible and performant than Db.Transact, but it comes with certain risks; for example, if there's a power outage or other hardware failure after the email is sent but before writing to the log, the email will be incorrect - even if the user got a confirmation, the order will not be in the database since it was never written to the transaction log.

If a handler creates multiple write transactions, use Db.TransactAsync and then wait for all transactions at once with Task.WaitAll or TaskFactory.ContinueWhenAll. Otherwise, the latency of the handler will degrade.

Consider the following example:

for (int i = 1; i <= 100000; i++)

{
    Db.Transact(() =>
    {
        new Person();
    });

}

// Execute some code after commiting and flushing all transactions

The result of the following code is exactly the same as the code above, but it's executed many times faster:

var tasks = new List<Task>();
for (int i = 1; i <= 100000; i++)

{
    tasks.Add(Db.TransactAsync(() =>

    {
        // All transactions up to this point have already been committed
        new Person();

    }));
}
Task.WaitAll(tasks.ToArray());
// Execute some code after commiting and flushing all the transactions

In this case, each iteration of the loop doesn't require the previous transaction to be flushed to the disk. The transaction execution is still synchronous.

Limitations

With the Db.TransactAsync API, it's tempting to use async/await in applications. Syntactically it's possible, although it's not that useful due to these limitations:

  1. Using async/await is not possible in a handler body as Handler API doesn't support async handlers.

  2. use async/await with caution as they may inadvertently increase the latency. Say if user code runs transactions sequentially, putting await in front of every Db.TransactAsync will accumulate all the individual latencies. The right stategy in this case is to make a list of tasks and then await them at once.

Transaction Size

Code in Db.Transact and Db.TransactAsync should execute in as short time as possible because conflicts are likelier the longer the transaction is. Conflicts requires long transactions to run more times which can be expensive. The solution is to break the transaction into smaller transactions.

No special measures have been taken to force after-await code to run on Starcounter threads, so manual Scheduling.ScheduleTask might be required (see for details).

Running background jobs
previous page
Db.TransactAsync