2014-09-22 3 views
5

Stackexchange.Redis에서 개체 및 인덱스 필드를 추가하는 코드가 있습니다. 트랜잭션 고정 스레드의 모든 메소드. 왜? 트랜잭션 내에서 실행StackExchange.Redis 트랜잭션 메서드가 고정됩니다.

var transaction = Database.CreateTransaction(); 

    //this line freeze thread. WHY ? 
    await transaction.StringSetAsync(KeyProvider.GetForID(obj.ID), PreSaveObject(obj)); 
    await transaction.HashSetAsync(emailKey, new[] { new HashEntry(obj.Email, Convert.ToString(obj.ID)) }); 

    return await transaction.ExecuteAsync(); 
+0

내가 아직 사용할 수 없다는 의미는 여기에 "대기 중"이라고 표시되어 있습니다. http://redis.io/topics/transactions –

답변

11

명령 후 트랜잭션을 실행 때까지 결과를 반환하지 않습니다. 이것은 단순히 Redis에서 트랜잭션이 작동하는 방식의 기능입니다. 트랜잭션 (transaction)이 실행될 때까지 로컬에서 버퍼링된다.) - 비록 그것이 보내 졌다고하더라도 : 결과는 은 트랜잭션이 완료 될 때까지는 이용 불가능하다.. 당신이 결과를 원하는 경우

, 당신은 (기다리고되지 않음) 작업을 저장하고, 그것을 후 을 기다리고해야 실행 :

var fooTask = tran.SomeCommandAsync(...); 
if(await tran.ExecuteAsync()) { 
    var foo = await fooTask; 
} 

이보기보다 저렴하는 것으로 : 트랜잭션이 실행될 때, 중첩 된 작업은 동시에 결과를 얻습니다. await은 해당 시나리오를 효율적으로 처리합니다.

+2

이상한 논리이지만 작동합니다! 고맙습니다! – boostivan

+0

@boostivan 익숙해지기 위해서는 약간의 생각이 필요합니다. note -이 작업을 수행하는 다른 방법은'Script *'를 사용하고 서버에서 ops를 수행하는 Lua 스크립트를 보내는 것입니다. 훨씬 더 간단합니다. –

+0

@MarcGravell 명령의 결과가 필요하지 않은 경우 가장 좋은 방법은 무엇입니까? 작업을 포착하고 어쨌든 거래 후에 '대기'하거나 불이나 잊어 버리십니까? (전자를 추측하지만 확신하고 싶습니다.) –

0

마크의 대답은 작동하지만 제 경우에는 상당한 양의 코드가 부풀어 오릅니다 (그리고 이런 식으로하는 것을 잊어 버리기 쉽습니다). 따라서 패턴을 적용하는 추상화를 생각해 냈습니다.

public static class RedisExtensions 
{ 
    public static async Task TransactAsync(this IDatabase db, Action<RedisCommandQueue> addCommands) 
    { 
     var tran = db.CreateTransaction(); 
     var q = new RedisCommandQueue(tran); 

     addCommands(q); 

     if (await tran.ExecuteAsync()) 
      await q.CompleteAsync(); 
    } 
} 

public class RedisCommandQueue 
{ 
    private readonly ITransaction _tran; 
    private readonly IList<Task> _tasks = new List<Task>(); 

    public RedisCommandQueue Enqueue(Func<ITransaction, Task> cmd) 
    { 
     _tasks.Add(cmd(_tran)); 
     return this; 
    } 

    internal RedisCommandQueue(ITransaction tran) => _tran = tran; 
    internal Task CompleteAsync() => Task.WhenAll(_tasks); 
} 

한 가지주의가 : 이것은 어떤의 결과에서 얻을 수있는 쉬운 방법을 제공하지 않습니다 여기에

await db.TransactAsync(commands => commands 
    .Enqueue(tran => tran.SomeCommandAsync(...)) 
    .Enqueue(tran => tran.SomeCommandAsync(...)) 
    .Enqueue(tran => tran.SomeCommandAsync(...))); 

이 구현의 : 여기

당신이 그것을 사용하는 방법 명령. 내 경우에는 (그리고 OP의) 괜찮습니다. 저는 항상 일련의 글쓰기를 위해 트랜잭션을 사용하고 있습니다. 나는이 코드가 내 코드를 잘라내는 데 정말로 도움이된다는 것을 발견했다. 을 Enqueue (작업을 반환해야 함)에만 노출시킴으로써 나는 그 때 해당 명령을 사용하지 말아야한다는 것을 "잊어 버릴"가능성이 적다. 나는 그들을 부른다.

관련 문제