NuGet Snippet:
Script Snippet (DG.StressTest.cmd):
Code Snippet (DG.StressTest.fsx):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
| #r @"System.Runtime.Serialization"
#r @"System.ServiceModel"
// nuget install -ExcludeVersion Microsoft.CrmSdk.CoreAssemblies
#r @"Microsoft.CrmSdk.CoreAssemblies\lib\net40\Microsoft.Xrm.Sdk.dll"
// nuget install -ExcludeVersion FSharp.Data
#r @"FSharp.Data\lib\net40\FSharp.Data.dll"
#load @"DG.Auth.fsx" // Just contains let usr = "usr" and let pwd = "pwd"
open System
open System.Runtime.Serialization
open System.ServiceModel.Description
open Microsoft.Xrm.Sdk
open Microsoft.Xrm.Sdk.Client
open Microsoft.Xrm.Sdk.Messages
open Microsoft.Xrm.Sdk.Query
open FSharp.Data
/// Utils
let r = new System.Random()
let crmCount (proxy:OrganizationServiceProxy) logicalName (date:DateTime) =
let f = FilterExpression()
f.AddCondition(@"createdon", ConditionOperator.GreaterEqual, date.ToUniversalTime())
let q = QueryExpression(logicalName)
q.ColumnSet <- ColumnSet(logicalName + "id")
q.Criteria <- f
q.PageInfo <- PagingInfo()
q.PageInfo.PageNumber <- 1
seq{ let resp = proxy.RetrieveMultiple(q)
yield! resp.Entities
let rec retrieveMultiple' (ec:EntityCollection) pn = seq{
match ec.MoreRecords with
| true ->
q.PageInfo.PageNumber <- (pn + 1)
q.PageInfo.PagingCookie <- ec.PagingCookie
let resp' = proxy.RetrieveMultiple(q)
yield! resp'.Entities
yield! retrieveMultiple' resp' (pn + 1)
| false -> () }
yield! retrieveMultiple' resp 1 }
|> Seq.length
/// Connection info:
let uri = Uri("https://org.api.crm4.dynamics.com/XRMServices/2011/Organization.svc");
let ac = AuthenticationCredentials()
ac.ClientCredentials.UserName.UserName <- DG.Auth.usr
ac.ClientCredentials.UserName.Password <- DG.Auth.pwd
let m = ServiceConfigurationFactory.CreateManagement<IOrganizationService>(uri)
let tc = m.Authenticate(ac)
let p = new OrganizationServiceProxy(m, tc.SecurityTokenResponse)
/// Test data:
let data = FreebaseData.GetDataContext()
let names = data.Society.People.``Family names`` |> Seq.take 250
let namesCache = names |> Seq.toArray |> Array.map(fun x -> x.Name)
let titles = data.``Products and Services``.Business.``Job titles`` |> Seq.take 250
let titlesCache = titles |> Seq.toArray |> Array.map(fun x -> x.Name)
let products = data.``Products and Services``.``Food & Drink``.Foods |> Seq.take 250
let productsCache = products |> Seq.toArray |> Array.map(fun x -> x.Name)
let countries = data.Commons.Location.Countries |> Seq.take 250
let countriesCache = countries |> Seq.toArray |> Array.map(fun x -> x.Name)
let cities = data.Commons.Location.``City/Town/Villages`` |> Seq.take 250
let citiesCache = cities |> Seq.toArray |> Array.map(fun x -> x.Name)
let companyName name = name + " Company"
let streetName name = name + " Street " + string(r.Next(1,1000))
let phoneNumber () = "555-" + string(r.Next(1000,10000))
let zipCode () = string(r.Next(1000,10000))
let email firstname lastname = (firstname + "." + lastname + "@mail.co.dk").ToLower()
/// Create as many accounts as possible
let createAccount () =
let a = Entity("account")
a.Attributes.Add("name",
namesCache.[r.Next(0,250)] + " " +
namesCache.[r.Next(0,250)] |> companyName)
a.Attributes.Add("telephone1", phoneNumber())
a.Attributes.Add("address1_line1", namesCache.[r.Next(0,250)] |> streetName)
a.Attributes.Add("address1_city", citiesCache.[r.Next(0,250)])
a.Attributes.Add("address1_postalcode", zipCode())
a.Attributes.Add("address1_country", countriesCache.[r.Next(0,250)])
a
// One account per thread
let createAccounts date concurrency =
Array.Parallel.init concurrency (fun _ -> createAccount ())
|> Array.Parallel.map(
fun x ->
try
match (date > DateTime.Now) with
| true ->
let p' = new OrganizationServiceProxy(m, tc.SecurityTokenResponse)
p'.Create(x) |> Some
| false -> None
with ex -> None)
// Ten accounts per thread
let createAccounts' date concurrency =
Array.Parallel.init concurrency
(fun _ ->
let em = new ExecuteMultipleRequest()
em.Settings <- new ExecuteMultipleSettings()
em.Settings.ContinueOnError <- true
em.Settings.ReturnResponses <- true
em.Requests <- new OrganizationRequestCollection()
em)
|> Array.Parallel.map(
fun x ->
try
Array.Parallel.init 10
(fun _ ->
let cr = new CreateRequest()
cr.Target <- createAccount()
x.Requests.Add(cr)) |> ignore
match (date > DateTime.Now) with
| true ->
let p' = new OrganizationServiceProxy(m, tc.SecurityTokenResponse)
p'.Execute(x) :?> ExecuteMultipleResponse |> Some
| false -> None
with ex -> None)
/// Stress Test
let rec stressTestCrm date concurrency =
match (date > DateTime.Now) with
| true ->
createAccounts' date concurrency |> ignore
stressTestCrm date concurrency
| false -> ()
/// Concurrent users (threads)
let concurrency = (1 <<< 10) // 1024
/// Start and Stop DateTimes
let startDate = DateTime.Now
let stopDate = startDate.AddMinutes(60.)
/// Perfom stress test and print outcome
stressTestCrm stopDate concurrency
(crmCount p "account" startDate, startDate.ToString("o"))
||> printfn "Accounts created: %i, since: %s"
|
Code result:
Architecture (Lenovo ThinkPad W540):