1

キューベースのビデオ アップロード システムのバックエンドとして RavenDB (2261) を使用しており、アップロード システムに関するさまざまな指標に関する「ライブ」SLA レポートを提供するよう求められています。

ドキュメントの形式は次のようになります。

{
  "ClipGuid": "01234567-1234-abcd-efef-123412341234",
  "CustomerId": "ABC123",
  "Title": "Shakespeare in Love",
  "DurationInSeconds": 82,
  "StateChanges": [
    {
      "OldState": "DoesNotExist",
      "NewState": "ReceivedFromUpload",
      "ChangedAt": "2013-03-15T15:38:38.7050002Z"
    },
    {
      "OldState": "ReceivedFromUpload",
      "NewState": "Validating",
      "ChangedAt": "2013-03-15T15:38:38.8453975Z"
    },
    {
      "OldState": "Validating",
      "NewState": "AwaitingSubmission",
      "ChangedAt": "2013-03-15T15:38:39.9529762Z"
    },
    {
      "OldState": "AwaitingSubmission",
      "NewState": "Submitted",
      "ChangedAt": "2013-03-15T15:38:43.4785084Z"
    },
    {
      "OldState": "Submitted",
      "NewState": "Playable",
      "ChangedAt": "2013-03-15T15:41:39.5523223Z"
    }
  ],
}

各 ClipInfo レコード内には、クリップが処理チェーンのある部分から別の部分に渡されるたびに追加される StateChanges のコレクションがあります。必要なのは、これらの StateChanges を 2 つの特定のタイムスパンに減らすことです。つまり、クリップが DoesNotExist から AwaitingSubmission に変化するのにかかった時間と、DoesNotExist から Playable に変わるのにかかった時間を知る必要があります。次に、これらの期間を日付/時刻でグループ化する必要があるため、次のような単純な SLA レポートを作成できます。

モックアップ動画アップロード SLA

必要な述語は LINQ ステートメントとして表現できますが、Raven クエリ内でこの種の複雑なロジックを指定しようとすると、空の結果 (または多数の DateTime.MinValue の結果) が返されるようです。

Raven のようなドキュメント データベースはレポート作成には理想的ではないことは理解しています。SQL へのレプリケーションやその他の種類のキャッシュ メカニズムを喜んで検討していますが、現時点では、データを抽出する方法が見当たりません。複数のクエリを実行してストアのコンテンツ全体を取得し、.NET で計算を実行します。

推奨事項はありますか?

ありがとう、

ディラン

4

1 に答える 1

0

私はあなたが調整する必要があるかもしれないいくつかの仮定をしました:

  • 厳密に UTC タイム ゾーンで操作します。「日」は UTC の午前 0 時から午前 0 時です。
  • あなたの週は日曜日から土曜日です
  • グループ化する日付は、報告された最初の状況報告日 (古い状態として「DoesNotExist」とマークされた日付) です。

グループ化する日付ブラケット (毎日、毎週、毎月) ごとに個別の map/reduce インデックスが必要になります。

開始日の定義方法を除いて、それらはほとんど同じです。創造力を働かせたい場合は、これらを一般的なインデックス定義にする方法を思いつくことができるかもしれませんが、RavenDB では常に3 つの個別のインデックスになってしまいます。

// This is the resulting class that all of these indexes will return
public class ClipStats
{
    public int CountClips { get; set; }
    public int NumPassedWithinTwentyPct { get; set; }
    public int NumPlayableWithinOneHour { get; set; }
    public DateTime Starting { get; set; }
}

public class ClipStats_ByDay : AbstractIndexCreationTask<ClipInfo, ClipStats>
{
    public ClipStats_ByDay()
    {
        Map = clips => from clip in clips
                        let state1 = clip.StateChanges.FirstOrDefault(x => x.OldState == "DoesNotExist")
                        let state2 = clip.StateChanges.FirstOrDefault(x => x.NewState == "AwaitingSubmission")
                        let state3 = clip.StateChanges.FirstOrDefault(x => x.NewState == "Playable")
                        let time1 = state2.ChangedAt - state1.ChangedAt
                        let time2 = state3.ChangedAt - state1.ChangedAt
                        select new
                        {
                            CountClips = 1,
                            NumPassedWithinTwentyPct = time1.TotalSeconds < clip.DurationInSeconds * 0.2 ? 1 : 0,
                            NumPlayableWithinOneHour = time2.TotalHours < 1 ? 1 : 0,
                            Starting = state1.ChangedAt.Date
                        };

        Reduce = results => from result in results
                            group result by result.Starting
                            into g
                            select new
                            {
                                CountClips = g.Sum(x => x.CountClips),
                                NumPassedWithinTwentyPct = g.Sum(x => x.NumPassedWithinTwentyPct),
                                NumPlayableWithinOneHour = g.Sum(x => x.NumPlayableWithinOneHour),
                                Starting = g.Key
                            };
    }
}

public class ClipStats_ByWeek : AbstractIndexCreationTask<ClipInfo, ClipStats>
{
    public ClipStats_ByWeek()
    {
        Map = clips => from clip in clips
                        let state1 = clip.StateChanges.FirstOrDefault(x => x.OldState == "DoesNotExist")
                        let state2 = clip.StateChanges.FirstOrDefault(x => x.NewState == "AwaitingSubmission")
                        let state3 = clip.StateChanges.FirstOrDefault(x => x.NewState == "Playable")
                        let time1 = state2.ChangedAt - state1.ChangedAt
                        let time2 = state3.ChangedAt - state1.ChangedAt
                        select new
                        {
                            CountClips = 1,
                            NumPassedWithinTwentyPct = time1.TotalSeconds < clip.DurationInSeconds * 0.2 ? 1 : 0,
                            NumPlayableWithinOneHour = time2.TotalHours < 1 ? 1 : 0,
                            Starting = state1.ChangedAt.Date.AddDays(0 - (int) state1.ChangedAt.Date.DayOfWeek)
                        };

        Reduce = results => from result in results
                            group result by result.Starting
                            into g
                            select new
                            {
                                CountClips = g.Sum(x => x.CountClips),
                                NumPassedWithinTwentyPct = g.Sum(x => x.NumPassedWithinTwentyPct),
                                NumPlayableWithinOneHour = g.Sum(x => x.NumPlayableWithinOneHour),
                                Starting = g.Key
                            };
    }
}

public class ClipStats_ByMonth : AbstractIndexCreationTask<ClipInfo, ClipStats>
{
    public ClipStats_ByMonth()
    {
        Map = clips => from clip in clips
                        let state1 = clip.StateChanges.FirstOrDefault(x => x.OldState == "DoesNotExist")
                        let state2 = clip.StateChanges.FirstOrDefault(x => x.NewState == "AwaitingSubmission")
                        let state3 = clip.StateChanges.FirstOrDefault(x => x.NewState == "Playable")
                        let time1 = state2.ChangedAt - state1.ChangedAt
                        let time2 = state3.ChangedAt - state1.ChangedAt
                        select new
                        {
                            CountClips = 1,
                            NumPassedWithinTwentyPct = time1.TotalSeconds < clip.DurationInSeconds * 0.2 ? 1 : 0,
                            NumPlayableWithinOneHour = time2.TotalHours < 1 ? 1 : 0,
                            Starting = state1.ChangedAt.Date.AddDays(1 - state1.ChangedAt.Date.Day)
                        };

        Reduce = results => from result in results
                            group result by result.Starting
                            into g
                            select new
                            {
                                CountClips = g.Sum(x => x.CountClips),
                                NumPassedWithinTwentyPct = g.Sum(x => x.NumPassedWithinTwentyPct),
                                NumPlayableWithinOneHour = g.Sum(x => x.NumPlayableWithinOneHour),
                                Starting = g.Key
                            };
    }
}

次に、クエリしたいとき...

var now = DateTime.UtcNow;

var today = now.Date;
var dailyStats = session.Query<ClipStats, ClipStats_ByDay>()
                        .FirstOrDefault(x => x.Starting == today);

var startOfWeek = today.AddDays(0 - (int) today.DayOfWeek);
var weeklyStats = session.Query<ClipStats, ClipStats_ByWeek>()
                         .FirstOrDefault(x => x.Starting == startOfWeek);

var startOfMonth = today.AddDays(1 - today.Day);
var monthlyStats = session.Query<ClipStats, ClipStats_ByMonth>()
                          .FirstOrDefault(x => x.Starting == startOfMonth);

結果には、totalsがあります。したがって、SLA のパーセント平均が必要な場合は、統計をカウントで割るだけです。これも返されます。

于 2013-03-18T22:25:58.443 に答える