Skip to content

Commit

Permalink
Raven cache is functional, but the recent-kills list crashes RavenDB …
Browse files Browse the repository at this point in the history
…with a StackOverFlowException.
  • Loading branch information
jtmueller committed Mar 2, 2012
1 parent a133278 commit 4960996
Show file tree
Hide file tree
Showing 18 changed files with 539 additions and 113 deletions.
44 changes: 44 additions & 0 deletions EveLib.RavenCache/CharacterQueries.fs
@@ -0,0 +1,44 @@
namespace EveLib.RavenCache

open System
open Raven.Client
open Raven.Client.Linq
open EveLib

type internal CharacterQueries(baseClient: FSharp.ICharQueries, store: IDocumentStore) =

let getAccountBalance charId = async {
use session = store.OpenAsyncSession()

let! wallets =
query {
for set in session.Query<WalletSet>() do
where (set.Type = WalletType.Personal && set.CharacterId = charId)
select set
} |> AsyncQuery.head

match wallets with
| None ->
let! updated = baseClient.GetAccountBalance(charId)
session.Store(updated)
do! session.AsyncSaveChanges()
return updated
| Some set when set.CachedUntil < DateTimeOffset.UtcNow ->
try
let! updated = baseClient.GetAccountBalance(charId)
session.Delete(set)
session.Store(updated)
do! session.AsyncSaveChanges()
return updated
with _ ->
return set
| Some set ->
return set
}

interface EveLib.FSharp.ICharQueries with
member x.GetAccountBalance(charId) = getAccountBalance charId
interface EveLib.Async.ICharQueries with
member x.GetAccountBalance(charId) = getAccountBalance charId |> Async.StartAsTask
interface EveLib.Sync.ICharQueries with
member x.GetAccountBalance(charId) = getAccountBalance charId |> Async.RunSynchronously
44 changes: 44 additions & 0 deletions EveLib.RavenCache/CorporationQueries.fs
@@ -0,0 +1,44 @@
namespace EveLib.RavenCache

open System
open Raven.Client
open Raven.Client.Linq
open EveLib

type internal CorporationQueries(baseClient: FSharp.ICorpQueries, store: IDocumentStore) =

let getAccountBalance charId = async {
use session = store.OpenAsyncSession()

let! wallets =
query {
for set in session.Query<WalletSet>() do
where (set.Type = WalletType.Corporate && set.CharacterId = charId)
select set
} |> AsyncQuery.head

match wallets with
| None ->
let! updated = baseClient.GetAccountBalance(charId)
session.Store(updated)
do! session.AsyncSaveChanges()
return updated
| Some set when set.CachedUntil < DateTimeOffset.UtcNow ->
try
let! updated = baseClient.GetAccountBalance(charId)
session.Delete(set)
session.Store(updated)
do! session.AsyncSaveChanges()
return updated
with _ ->
return set
| Some set ->
return set
}

interface EveLib.FSharp.ICorpQueries with
member x.GetAccountBalance(charId) = getAccountBalance charId
interface EveLib.Async.ICorpQueries with
member x.GetAccountBalance(charId) = getAccountBalance charId |> Async.StartAsTask
interface EveLib.Sync.ICorpQueries with
member x.GetAccountBalance(charId) = getAccountBalance charId |> Async.RunSynchronously
6 changes: 6 additions & 0 deletions EveLib.RavenCache/EveLib.RavenCache.fsproj
Expand Up @@ -39,6 +39,11 @@
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<ItemGroup>
<Compile Include="RavenExt.fs" />
<Compile Include="CharacterQueries.fs" />
<Compile Include="CorporationQueries.fs" />
<Compile Include="EveQueries.fs" />
<Compile Include="MapQueries.fs" />
<Compile Include="RavenEveClient.fs" />
<None Include="Script.fsx" />
<None Include="packages.config" />
</ItemGroup>
Expand Down Expand Up @@ -88,6 +93,7 @@
<Reference Include="System.ServiceModel.Activities" />
<Reference Include="System.ServiceModel.Discovery" />
<Reference Include="System.Transactions" />
<Reference Include="System.Xml.Linq" />
<Reference Include="WindowsBase" />
<ProjectReference Include="..\EveLib\EveLib.fsproj">
<Name>EveLib</Name>
Expand Down
113 changes: 113 additions & 0 deletions EveLib.RavenCache/EveQueries.fs
@@ -0,0 +1,113 @@
namespace EveLib.RavenCache

open System
open System.Linq
open Raven.Client
open Raven.Client.Linq
open EveLib

// this breaks because "contains" is not supported by RavenDB

type internal EveQueries(baseClient: FSharp.IEveQueries, store: IDocumentStore) =

let getItemNames (ids:int[]) = async {
use session = store.OpenAsyncSession()

// TODO: raven returns batches of 128 only.
// need to detect if more id's requested, use paging.
let! matches =
query {
for ni in session.Query<NamedItem>() do
where (LinqExtensions.In(ni.ItemId, ids))
select ni
} |> AsyncQuery.asIList

if matches.Count = 0 then
let! updated = baseClient.GetItemNames(ids)
updated |> Seq.iter session.Store
do! session.AsyncSaveChanges()
return updated
else
let outdated, stillGood =
let now = DateTimeOffset.UtcNow
matches
|> List.ofSeq
|> List.partition (fun m -> m.CachedUntil < now)

let missing =
stillGood
|> Seq.map (fun m -> m.ItemId)
|> Set.ofSeq
|> Set.difference (ids |> Set.ofArray)

let toLoad =
outdated
|> Seq.map (fun m -> m.ItemId)
|> Seq.append missing

if toLoad |> Seq.exists (fun _ -> true) then
let! updated = baseClient.GetItemNames(toLoad |> Array.ofSeq)
let merged = stillGood |> Seq.append updated |> Seq.cache
outdated |> List.iter session.Delete
merged |> Seq.iter session.Store
do! session.AsyncSaveChanges()
return merged
else
return upcast stillGood
}

let getItemIds (names:string[]) = async {
use session = store.OpenAsyncSession()

// TODO: raven returns batches of 128 only.
// need to detect if more id's requested, use paging.
let! matches =
query {
for ni in session.Query<NamedItem>() do
where (LinqExtensions.In(ni.Name, names))
select ni
} |> AsyncQuery.asIList

if matches.Count = 0 then
let! updated = baseClient.GetItemIds(names)
updated |> Seq.iter session.Store
do! session.AsyncSaveChanges()
return updated
else
let outdated, stillGood =
let now = DateTimeOffset.UtcNow
matches
|> List.ofSeq
|> List.partition (fun m -> m.CachedUntil < now)

let missing =
stillGood
|> Seq.map (fun m -> m.Name)
|> Set.ofSeq
|> Set.difference (names |> Set.ofArray)

let toLoad =
outdated
|> Seq.map (fun m -> m.Name)
|> Seq.append missing

if toLoad |> Seq.exists (fun _ -> true) then
let! updated = baseClient.GetItemIds(toLoad |> Array.ofSeq)
let merged = stillGood |> Seq.append updated |> Seq.cache
outdated |> List.iter session.Delete
merged |> Seq.iter session.Store
do! session.AsyncSaveChanges()
return merged
else
return upcast stillGood
}

interface EveLib.FSharp.IEveQueries with
member x.GetItemIds([<ParamArray>] names : string[]) = getItemIds names
member x.GetItemNames([<ParamArray>] ids : int[]) = getItemNames ids
interface EveLib.Async.IEveQueries with
member x.GetItemIds([<ParamArray>] names : string[]) = getItemIds names |> Async.StartAsTask
member x.GetItemNames([<ParamArray>] ids : int[]) = getItemNames ids |> Async.StartAsTask
interface EveLib.Sync.IEveQueries with
member x.GetItemIds([<ParamArray>] names : string[]) = getItemIds names |> Async.RunSynchronously
member x.GetItemNames([<ParamArray>] ids : int[]) = getItemNames ids |> Async.RunSynchronously
37 changes: 37 additions & 0 deletions EveLib.RavenCache/MapQueries.fs
@@ -0,0 +1,37 @@
namespace EveLib.RavenCache

open System
open Raven.Client
open Raven.Client.Linq
open EveLib

type internal MapQueries(baseClient: FSharp.IMapQueries, store: IDocumentStore) =

// TODO: this crashes RavenDB with a Stack Overflow Exception
let getRecentKills () = async {
use session = store.OpenAsyncSession()

let! kills = session.Query<RecentKills>() |> AsyncQuery.head

match kills with
| None ->
let! updated = baseClient.GetRecentKills()
session.Store(updated)
do! session.AsyncSaveChanges()
return updated
| Some k when k.CachedUntil < DateTimeOffset.UtcNow ->
let! updated = baseClient.GetRecentKills()
session.Delete(k)
session.Store(updated)
do! session.AsyncSaveChanges()
return updated
| Some k ->
return k
}

interface EveLib.FSharp.IMapQueries with
member x.GetRecentKills() = getRecentKills()
interface EveLib.Async.IMapQueries with
member x.GetRecentKills() = getRecentKills() |> Async.StartAsTask
interface EveLib.Sync.IMapQueries with
member x.GetRecentKills() = getRecentKills() |> Async.RunSynchronously
98 changes: 98 additions & 0 deletions EveLib.RavenCache/RavenEveClient.fs
@@ -0,0 +1,98 @@
namespace EveLib.RavenCache

open System
open Raven.Client
open Raven.Client.Linq
open EveLib

type RavenEveClient(apiKey:ApiKey) =

static let store = Document.DocumentStore.OpenInitializedStore()

let baseClient = EveClient.CreateFSharp apiKey
let character = lazy( CharacterQueries(baseClient.Character, store) )
let corporation = lazy( CorporationQueries(baseClient.Corporation, store) )
let eve = lazy( EveQueries(baseClient.Eve, store) )
let map = lazy( MapQueries(baseClient.Map, store) )

let getCharacters () = async {
use session = store.OpenAsyncSession()

let! charList =
query {
for cl in session.Query<CharacterList>() do
where (cl.KeyId = apiKey.Id)
select cl
take 1
} |> AsyncQuery.head

match charList with
| None ->
let! updated = baseClient.GetCharacters()
session.Store(updated)
do! session.AsyncSaveChanges()
return updated
| Some cl when cl.CachedUntil < DateTimeOffset.UtcNow ->
try
let! updated = baseClient.GetCharacters()
session.Delete(cl)
session.Store(updated)
do! session.AsyncSaveChanges()
return updated
with _ ->
return cl
| Some cl ->
return cl
}

let getServerStatus () = async {
use session = store.OpenAsyncSession()

let! status = session.Query<ServerStatus>() |> AsyncQuery.head

match status with
| None ->
let! updated = baseClient.GetServerStatus()
session.Store(updated)
do! session.AsyncSaveChanges()
return updated
| Some st when st.CachedUntil < DateTimeOffset.UtcNow ->
try
let! updated = baseClient.GetServerStatus()
session.Delete(st)
session.Store(updated)
do! session.AsyncSaveChanges()
return updated
with _ ->
return st
| Some st ->
return st
}

interface EveLib.FSharp.IEveClient with
member x.GetCharacters() = getCharacters()
member x.GetServerStatus() = getServerStatus()
member x.Character = upcast character.Value
member x.Corporation = upcast corporation.Value
member x.Eve = upcast eve.Value
member x.Map = upcast map.Value

interface EveLib.Async.IEveClient with
member x.GetCharacters() = getCharacters() |> Async.StartAsTask
member x.GetServerStatus() = getServerStatus() |> Async.StartAsTask
member x.Character = upcast character.Value
member x.Corporation = upcast corporation.Value
member x.Eve = upcast eve.Value
member x.Map = upcast map.Value

interface EveLib.Sync.IEveClient with
member x.GetCharacters() = getCharacters() |> Async.RunSynchronously
member x.GetServerStatus() = getServerStatus() |> Async.RunSynchronously
member x.Character = upcast character.Value
member x.Corporation = upcast corporation.Value
member x.Eve = upcast eve.Value
member x.Map = upcast map.Value

static member CreateFSharp apiKey store = RavenEveClient(apiKey) :> EveLib.FSharp.IEveClient
static member CreateAsync apiKey store = RavenEveClient(apiKey) :> EveLib.Async.IEveClient
static member CreateSync apiKey = RavenEveClient(apiKey) :> EveLib.Sync.IEveClient

0 comments on commit 4960996

Please sign in to comment.