├── .gitignore ├── AppClient ├── AppClient.csproj ├── OrleansAdoNetContent │ ├── MySQL │ │ ├── MySQL-Clustering.sql │ │ └── MySQL-Main.sql │ ├── Oracle │ │ ├── Oracle-Clustering.sql │ │ └── Oracle-Main.sql │ ├── PostgreSQL │ │ ├── PostgreSQL-Clustering.sql │ │ └── PostgreSQL-Main.sql │ └── SQLServer │ │ ├── SQLServer-Clustering.sql │ │ └── SQLServer-Main.sql └── Program.cs ├── AppCloud ├── AppCloud.sfproj ├── ApplicationPackageRoot │ └── ApplicationManifest.xml ├── ApplicationParameters │ ├── Cloud.xml │ ├── Local.1Node.xml │ └── Local.5Node.xml ├── PublishProfiles │ ├── Cloud.xml │ ├── Local.1Node.xml │ └── Local.5Node.xml ├── Scripts │ └── Deploy-FabricApplication.ps1 └── packages.config ├── AppHost ├── AppHost.csproj ├── OrleansAdoNetContent │ ├── MySQL │ │ ├── MySQL-Clustering.sql │ │ └── MySQL-Main.sql │ ├── Oracle │ │ ├── Oracle-Clustering.sql │ │ └── Oracle-Main.sql │ ├── PostgreSQL │ │ ├── PostgreSQL-Clustering.sql │ │ └── PostgreSQL-Main.sql │ └── SQLServer │ │ ├── SQLServer-Clustering.sql │ │ └── SQLServer-Main.sql └── Program.cs ├── AppWebClient ├── AppWebClient.csproj ├── Controllers │ └── HomeController.cs ├── OrleansAdoNetContent │ ├── MySQL │ │ ├── MySQL-Clustering.sql │ │ └── MySQL-Main.sql │ ├── Oracle │ │ ├── Oracle-Clustering.sql │ │ └── Oracle-Main.sql │ ├── PostgreSQL │ │ ├── PostgreSQL-Clustering.sql │ │ └── PostgreSQL-Main.sql │ └── SQLServer │ │ ├── SQLServer-Clustering.sql │ │ └── SQLServer-Main.sql ├── Program.cs ├── Startup.cs ├── appsettings.Development.json └── appsettings.json ├── Demo.Grain ├── Demo.Grain.csproj └── HelloGrain .cs ├── Demo.IGrain ├── Demo.IGrain.csproj └── IHello.cs ├── Demo.sln ├── Lib ├── ClientFactory.cs ├── GlobalConfig.cs ├── IClientFactory.cs └── Lib.csproj ├── README.md ├── StatelessHost ├── OrleansAdoNetContent │ ├── MySQL │ │ ├── MySQL-Clustering.sql │ │ └── MySQL-Main.sql │ ├── Oracle │ │ ├── Oracle-Clustering.sql │ │ └── Oracle-Main.sql │ ├── PostgreSQL │ │ ├── PostgreSQL-Clustering.sql │ │ └── PostgreSQL-Main.sql │ └── SQLServer │ │ ├── SQLServer-Clustering.sql │ │ └── SQLServer-Main.sql ├── PackageRoot │ ├── Config │ │ └── Settings.xml │ └── ServiceManifest.xml ├── Program.cs ├── ServiceEventSource.cs ├── StartupTask.cs ├── StatelessHost.cs ├── StatelessHost.csproj └── appsettings.json └── StatelessWebGo ├── Controllers └── HomeController.cs ├── OrleansAdoNetContent ├── MySQL │ ├── MySQL-Clustering.sql │ └── MySQL-Main.sql ├── Oracle │ ├── Oracle-Clustering.sql │ └── Oracle-Main.sql ├── PostgreSQL │ ├── PostgreSQL-Clustering.sql │ └── PostgreSQL-Main.sql └── SQLServer │ ├── SQLServer-Clustering.sql │ └── SQLServer-Main.sql ├── PackageRoot ├── Config │ └── Settings.xml └── ServiceManifest.xml ├── Program.cs ├── ServiceEventSource.cs ├── Startup.cs ├── StatelessWebGo.cs ├── StatelessWebGo.csproj ├── appsettings.Development.json └── appsettings.json /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | -------------------------------------------------------------------------------- /AppClient/AppClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /AppClient/OrleansAdoNetContent/MySQL/MySQL-Clustering.sql: -------------------------------------------------------------------------------- 1 | -- For each deployment, there will be only one (active) membership version table version column which will be updated periodically. 2 | CREATE TABLE OrleansMembershipVersionTable 3 | ( 4 | DeploymentId NVARCHAR(150) NOT NULL, 5 | Timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 6 | Version INT NOT NULL DEFAULT 0, 7 | 8 | CONSTRAINT PK_OrleansMembershipVersionTable_DeploymentId PRIMARY KEY(DeploymentId) 9 | ); 10 | 11 | -- Every silo instance has a row in the membership table. 12 | CREATE TABLE OrleansMembershipTable 13 | ( 14 | DeploymentId NVARCHAR(150) NOT NULL, 15 | Address VARCHAR(45) NOT NULL, 16 | Port INT NOT NULL, 17 | Generation INT NOT NULL, 18 | SiloName NVARCHAR(150) NOT NULL, 19 | HostName NVARCHAR(150) NOT NULL, 20 | Status INT NOT NULL, 21 | ProxyPort INT NULL, 22 | SuspectTimes VARCHAR(8000) NULL, 23 | StartTime DATETIME NOT NULL, 24 | IAmAliveTime DATETIME NOT NULL, 25 | 26 | CONSTRAINT PK_MembershipTable_DeploymentId PRIMARY KEY(DeploymentId, Address, Port, Generation), 27 | CONSTRAINT FK_MembershipTable_MembershipVersionTable_DeploymentId FOREIGN KEY (DeploymentId) REFERENCES OrleansMembershipVersionTable (DeploymentId) 28 | ); 29 | 30 | INSERT INTO OrleansQuery(QueryKey, QueryText) 31 | VALUES 32 | ( 33 | 'UpdateIAmAlivetimeKey',' 34 | -- This is expected to never fail by Orleans, so return value 35 | -- is not needed nor is it checked. 36 | UPDATE OrleansMembershipTable 37 | SET 38 | IAmAliveTime = @IAmAliveTime 39 | WHERE 40 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 41 | AND Address = @Address AND @Address IS NOT NULL 42 | AND Port = @Port AND @Port IS NOT NULL 43 | AND Generation = @Generation AND @Generation IS NOT NULL; 44 | '); 45 | 46 | INSERT INTO OrleansQuery(QueryKey, QueryText) 47 | VALUES 48 | ( 49 | 'InsertMembershipVersionKey',' 50 | INSERT INTO OrleansMembershipVersionTable 51 | ( 52 | DeploymentId 53 | ) 54 | SELECT * FROM ( SELECT @DeploymentId ) AS TMP 55 | WHERE NOT EXISTS 56 | ( 57 | SELECT 1 58 | FROM 59 | OrleansMembershipVersionTable 60 | WHERE 61 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 62 | ); 63 | 64 | SELECT ROW_COUNT(); 65 | '); 66 | 67 | INSERT INTO OrleansQuery(QueryKey, QueryText) 68 | VALUES 69 | ( 70 | 'InsertMembershipKey',' 71 | call InsertMembershipKey(@DeploymentId, @Address, @Port, @Generation, 72 | @Version, @SiloName, @HostName, @Status, @ProxyPort, @StartTime, @IAmAliveTime);' 73 | ); 74 | 75 | DELIMITER $$ 76 | 77 | CREATE PROCEDURE InsertMembershipKey( 78 | in _DeploymentId NVARCHAR(150), 79 | in _Address VARCHAR(45), 80 | in _Port INT, 81 | in _Generation INT, 82 | in _Version INT, 83 | in _SiloName NVARCHAR(150), 84 | in _HostName NVARCHAR(150), 85 | in _Status INT, 86 | in _ProxyPort INT, 87 | in _StartTime DATETIME, 88 | in _IAmAliveTime DATETIME 89 | ) 90 | BEGIN 91 | DECLARE _ROWCOUNT INT; 92 | START TRANSACTION; 93 | INSERT INTO OrleansMembershipTable 94 | ( 95 | DeploymentId, 96 | Address, 97 | Port, 98 | Generation, 99 | SiloName, 100 | HostName, 101 | Status, 102 | ProxyPort, 103 | StartTime, 104 | IAmAliveTime 105 | ) 106 | SELECT * FROM ( SELECT 107 | _DeploymentId, 108 | _Address, 109 | _Port, 110 | _Generation, 111 | _SiloName, 112 | _HostName, 113 | _Status, 114 | _ProxyPort, 115 | _StartTime, 116 | _IAmAliveTime) AS TMP 117 | WHERE NOT EXISTS 118 | ( 119 | SELECT 1 120 | FROM 121 | OrleansMembershipTable 122 | WHERE 123 | DeploymentId = _DeploymentId AND _DeploymentId IS NOT NULL 124 | AND Address = _Address AND _Address IS NOT NULL 125 | AND Port = _Port AND _Port IS NOT NULL 126 | AND Generation = _Generation AND _Generation IS NOT NULL 127 | ); 128 | 129 | UPDATE OrleansMembershipVersionTable 130 | SET 131 | Version = Version + 1 132 | WHERE 133 | DeploymentId = _DeploymentId AND _DeploymentId IS NOT NULL 134 | AND Version = _Version AND _Version IS NOT NULL 135 | AND ROW_COUNT() > 0; 136 | 137 | SET _ROWCOUNT = ROW_COUNT(); 138 | 139 | IF _ROWCOUNT = 0 140 | THEN 141 | ROLLBACK; 142 | ELSE 143 | COMMIT; 144 | END IF; 145 | SELECT _ROWCOUNT; 146 | END$$ 147 | 148 | DELIMITER ; 149 | 150 | INSERT INTO OrleansQuery(QueryKey, QueryText) 151 | VALUES 152 | ( 153 | 'UpdateMembershipKey',' 154 | START TRANSACTION; 155 | 156 | UPDATE OrleansMembershipVersionTable 157 | SET 158 | Version = Version + 1 159 | WHERE 160 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 161 | AND Version = @Version AND @Version IS NOT NULL; 162 | 163 | UPDATE OrleansMembershipTable 164 | SET 165 | Status = @Status, 166 | SuspectTimes = @SuspectTimes, 167 | IAmAliveTime = @IAmAliveTime 168 | WHERE 169 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 170 | AND Address = @Address AND @Address IS NOT NULL 171 | AND Port = @Port AND @Port IS NOT NULL 172 | AND Generation = @Generation AND @Generation IS NOT NULL 173 | AND ROW_COUNT() > 0; 174 | 175 | SELECT ROW_COUNT(); 176 | COMMIT; 177 | '); 178 | 179 | INSERT INTO OrleansQuery(QueryKey, QueryText) 180 | VALUES 181 | ( 182 | 'GatewaysQueryKey',' 183 | SELECT 184 | Address, 185 | ProxyPort, 186 | Generation 187 | FROM 188 | OrleansMembershipTable 189 | WHERE 190 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 191 | AND Status = @Status AND @Status IS NOT NULL 192 | AND ProxyPort > 0; 193 | '); 194 | 195 | INSERT INTO OrleansQuery(QueryKey, QueryText) 196 | VALUES 197 | ( 198 | 'MembershipReadRowKey',' 199 | SELECT 200 | v.DeploymentId, 201 | m.Address, 202 | m.Port, 203 | m.Generation, 204 | m.SiloName, 205 | m.HostName, 206 | m.Status, 207 | m.ProxyPort, 208 | m.SuspectTimes, 209 | m.StartTime, 210 | m.IAmAliveTime, 211 | v.Version 212 | FROM 213 | OrleansMembershipVersionTable v 214 | -- This ensures the version table will returned even if there is no matching membership row. 215 | LEFT OUTER JOIN OrleansMembershipTable m ON v.DeploymentId = m.DeploymentId 216 | AND Address = @Address AND @Address IS NOT NULL 217 | AND Port = @Port AND @Port IS NOT NULL 218 | AND Generation = @Generation AND @Generation IS NOT NULL 219 | WHERE 220 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 221 | '); 222 | 223 | INSERT INTO OrleansQuery(QueryKey, QueryText) 224 | VALUES 225 | ( 226 | 'MembershipReadAllKey',' 227 | SELECT 228 | v.DeploymentId, 229 | m.Address, 230 | m.Port, 231 | m.Generation, 232 | m.SiloName, 233 | m.HostName, 234 | m.Status, 235 | m.ProxyPort, 236 | m.SuspectTimes, 237 | m.StartTime, 238 | m.IAmAliveTime, 239 | v.Version 240 | FROM 241 | OrleansMembershipVersionTable v LEFT OUTER JOIN OrleansMembershipTable m 242 | ON v.DeploymentId = m.DeploymentId 243 | WHERE 244 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 245 | '); 246 | 247 | INSERT INTO OrleansQuery(QueryKey, QueryText) 248 | VALUES 249 | ( 250 | 'DeleteMembershipTableEntriesKey',' 251 | DELETE FROM OrleansMembershipTable 252 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 253 | DELETE FROM OrleansMembershipVersionTable 254 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 255 | '); 256 | -------------------------------------------------------------------------------- /AppClient/OrleansAdoNetContent/MySQL/MySQL-Main.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation notes: 3 | 4 | 1) The general idea is that data is read and written through Orleans specific queries. 5 | Orleans operates on column names and types when reading and on parameter names and types when writing. 6 | 7 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 8 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 9 | is maintained. 10 | 11 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 12 | by virtue of uniform naming across concrete implementations. 13 | 14 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 15 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 16 | 17 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 18 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 19 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 20 | Orleans handles exception as a failure and will retry. 21 | 22 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 23 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Runtime-Tables.html 24 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Cluster-Management 25 | https://github.com/dotnet/orleans/blob/master/src/Orleans/SystemTargetInterfaces/IMembershipTable.cs 26 | */ 27 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 28 | -- these are the only queries Orleans issues to the database. 29 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 30 | CREATE TABLE OrleansQuery 31 | ( 32 | QueryKey VARCHAR(64) NOT NULL, 33 | QueryText VARCHAR(8000) NOT NULL, 34 | 35 | CONSTRAINT OrleansQuery_Key PRIMARY KEY(QueryKey) 36 | ); 37 | -------------------------------------------------------------------------------- /AppClient/OrleansAdoNetContent/Oracle/Oracle-Clustering.sql: -------------------------------------------------------------------------------- 1 | -- For each deployment, there will be only one (active) membership version table version column which will be updated periodically. 2 | CREATE TABLE "ORLEANSMEMBERSHIPVERSIONTABLE" 3 | ( 4 | "DEPLOYMENTID" NVARCHAR2(150) NOT NULL ENABLE, 5 | "TIMESTAMP" TIMESTAMP (6) DEFAULT sys_extract_utc(systimestamp) NOT NULL ENABLE, 6 | "VERSION" NUMBER(*,0) DEFAULT 0, 7 | 8 | CONSTRAINT "ORLEANSMEMBERSHIPVERSIONTA_PK" PRIMARY KEY ("DEPLOYMENTID") 9 | ); 10 | / 11 | 12 | -- Every silo instance has a row in the membership table. 13 | CREATE TABLE "ORLEANSMEMBERSHIPTABLE" 14 | ( 15 | "DEPLOYMENTID" NVARCHAR2(150) NOT NULL ENABLE, 16 | "ADDRESS" VARCHAR2(45 BYTE) NOT NULL ENABLE, 17 | "PORT" NUMBER(*,0) NOT NULL ENABLE, 18 | "GENERATION" NUMBER(*,0) NOT NULL ENABLE, 19 | "SILONAME" NVARCHAR2(150) NOT NULL ENABLE, 20 | "HOSTNAME" NVARCHAR2(150) NOT NULL ENABLE, 21 | "STATUS" NUMBER(*,0) NOT NULL ENABLE, 22 | "PROXYPORT" NUMBER(*,0), 23 | "SUSPECTTIMES" VARCHAR2(4000 BYTE), 24 | "STARTTIME" TIMESTAMP (6) NOT NULL ENABLE, 25 | "IAMALIVETIME" TIMESTAMP (6) NOT NULL ENABLE, 26 | 27 | CONSTRAINT "ORLEANSMEMBERSHIPTABLE_PK" PRIMARY KEY ("DEPLOYMENTID", "ADDRESS", "PORT", "GENERATION"), 28 | CONSTRAINT "ORLEANSMEMBERSHIPTABLE_FK1" FOREIGN KEY ("DEPLOYMENTID") 29 | REFERENCES "ORLEANSMEMBERSHIPVERSIONTABLE" ("DEPLOYMENTID") ENABLE 30 | ); 31 | / 32 | 33 | CREATE OR REPLACE FUNCTION InsertMembership(PARAM_DEPLOYMENTID IN NVARCHAR2, PARAM_IAMALIVETIME IN TIMESTAMP, PARAM_SILONAME IN NVARCHAR2, PARAM_HOSTNAME IN NVARCHAR2, PARAM_ADDRESS IN VARCHAR2, 34 | PARAM_PORT IN NUMBER, PARAM_GENERATION IN NUMBER, PARAM_STARTTIME IN TIMESTAMP, PARAM_STATUS IN NUMBER, PARAM_PROXYPORT IN NUMBER, PARAM_VERSION IN NUMBER) 35 | RETURN NUMBER IS 36 | rowcount NUMBER; 37 | PRAGMA AUTONOMOUS_TRANSACTION; 38 | BEGIN 39 | INSERT INTO OrleansMembershipTable 40 | ( 41 | DeploymentId, 42 | Address, 43 | Port, 44 | Generation, 45 | SiloName, 46 | HostName, 47 | Status, 48 | ProxyPort, 49 | StartTime, 50 | IAmAliveTime 51 | ) 52 | SELECT 53 | PARAM_DEPLOYMENTID, 54 | PARAM_ADDRESS, 55 | PARAM_PORT, 56 | PARAM_GENERATION, 57 | PARAM_SILONAME, 58 | PARAM_HOSTNAME, 59 | PARAM_STATUS, 60 | PARAM_PROXYPORT, 61 | PARAM_STARTTIME, 62 | PARAM_IAMALIVETIME 63 | FROM DUAL WHERE NOT EXISTS 64 | ( 65 | SELECT 1 FROM OrleansMembershipTable WHERE 66 | DeploymentId = PARAM_DEPLOYMENTID AND PARAM_DEPLOYMENTID IS NOT NULL 67 | AND Address = PARAM_ADDRESS AND PARAM_ADDRESS IS NOT NULL 68 | AND Port = PARAM_PORT AND PARAM_PORT IS NOT NULL 69 | AND Generation = PARAM_GENERATION AND PARAM_GENERATION IS NOT NULL 70 | ); 71 | rowcount := SQL%ROWCOUNT; 72 | UPDATE OrleansMembershipVersionTable 73 | SET Timestamp = sys_extract_utc(systimestamp), 74 | Version = Version + 1 75 | WHERE 76 | DeploymentId = PARAM_DEPLOYMENTID AND PARAM_DEPLOYMENTID IS NOT NULL 77 | AND Version = PARAM_VERSION AND PARAM_VERSION IS NOT NULL 78 | AND rowcount > 0; 79 | rowcount := SQL%ROWCOUNT; 80 | IF rowcount = 0 THEN 81 | ROLLBACK; 82 | ELSE 83 | COMMIT; 84 | END IF; 85 | 86 | IF rowcount > 0 THEN 87 | RETURN(1); 88 | ELSE 89 | RETURN(0); 90 | END IF; 91 | END; 92 | / 93 | 94 | CREATE OR REPLACE FUNCTION UpdateMembership(PARAM_DEPLOYMENTID IN NVARCHAR2, PARAM_ADDRESS IN VARCHAR2, PARAM_PORT IN NUMBER, PARAM_GENERATION IN NUMBER, 95 | PARAM_IAMALIVETIME IN TIMESTAMP, PARAM_STATUS IN NUMBER, PARAM_SUSPECTTIMES IN VARCHAR2, PARAM_VERSION IN NUMBER 96 | ) 97 | RETURN NUMBER IS 98 | rowcount NUMBER; 99 | PRAGMA AUTONOMOUS_TRANSACTION; 100 | BEGIN 101 | UPDATE OrleansMembershipVersionTable 102 | SET 103 | Timestamp = sys_extract_utc(systimestamp), 104 | Version = Version + 1 105 | WHERE 106 | DeploymentId = PARAM_DEPLOYMENTID AND PARAM_DEPLOYMENTID IS NOT NULL 107 | AND Version = PARAM_VERSION AND PARAM_VERSION IS NOT NULL; 108 | rowcount := SQL%ROWCOUNT; 109 | UPDATE OrleansMembershipTable 110 | SET 111 | Status = PARAM_STATUS, 112 | SuspectTimes = PARAM_SUSPECTTIMES, 113 | IAmAliveTime = PARAM_IAMALIVETIME 114 | WHERE DeploymentId = PARAM_DEPLOYMENTID AND PARAM_DEPLOYMENTID IS NOT NULL 115 | AND Address = PARAM_ADDRESS AND PARAM_ADDRESS IS NOT NULL 116 | AND Port = PARAM_PORT AND PARAM_PORT IS NOT NULL 117 | AND Generation = PARAM_GENERATION AND PARAM_GENERATION IS NOT NULL 118 | AND rowcount > 0; 119 | rowcount := SQL%ROWCOUNT; 120 | COMMIT; 121 | RETURN(rowcount); 122 | END; 123 | / 124 | 125 | CREATE OR REPLACE FUNCTION InsertMembershipVersion(PARAM_DEPLOYMENTID IN NVARCHAR2) 126 | RETURN NUMBER IS 127 | rowcount NUMBER; 128 | PRAGMA AUTONOMOUS_TRANSACTION; 129 | BEGIN 130 | INSERT INTO OrleansMembershipVersionTable 131 | ( 132 | DeploymentId 133 | ) 134 | SELECT PARAM_DEPLOYMENTID FROM DUAL WHERE NOT EXISTS 135 | ( 136 | SELECT 1 FROM OrleansMembershipVersionTable WHERE 137 | DeploymentId = PARAM_DEPLOYMENTID AND PARAM_DEPLOYMENTID IS NOT NULL 138 | ); 139 | rowCount := SQL%ROWCOUNT; 140 | 141 | COMMIT; 142 | RETURN(rowCount); 143 | END; 144 | / 145 | 146 | CREATE OR REPLACE FUNCTION UpdateIAmAlivetime(PARAM_DEPLOYMENTID IN NVARCHAR2, PARAM_ADDRESS in VARCHAR2, PARAM_PORT IN NUMBER, 147 | PARAM_GENERATION IN NUMBER, PARAM_IAMALIVE IN TIMESTAMP) 148 | RETURN NUMBER IS 149 | rowcount NUMBER; 150 | PRAGMA AUTONOMOUS_TRANSACTION; 151 | BEGIN 152 | UPDATE OrleansMembershipTable 153 | SET 154 | IAmAliveTime = PARAM_IAMALIVE 155 | WHERE 156 | DeploymentId = PARAM_DEPLOYMENTID AND PARAM_DEPLOYMENTID IS NOT NULL 157 | AND Address = PARAM_ADDRESS AND PARAM_ADDRESS IS NOT NULL 158 | AND Port = PARAM_PORT AND PARAM_PORT IS NOT NULL 159 | AND Generation = PARAM_GENERATION AND PARAM_GENERATION IS NOT NULL; 160 | COMMIT; 161 | RETURN(0); 162 | END; 163 | / 164 | 165 | INSERT INTO OrleansQuery(QueryKey, QueryText) 166 | VALUES 167 | ( 168 | 'UpdateIAmAlivetimeKey',' 169 | SELECT UpdateIAmAlivetime(:DeploymentId, :Address, :Port, :Generation, :IAmAliveTime) AS RESULT FROM DUAL 170 | '); 171 | / 172 | 173 | INSERT INTO OrleansQuery(QueryKey, QueryText) 174 | VALUES 175 | ( 176 | 'InsertMembershipVersionKey',' 177 | SELECT InsertMembershipVersion(:DeploymentId) AS RESULT FROM DUAL 178 | '); 179 | / 180 | 181 | INSERT INTO OrleansQuery(QueryKey, QueryText) 182 | VALUES 183 | ( 184 | 'InsertMembershipKey',' 185 | SELECT INSERTMEMBERSHIP(:DeploymentId,:IAmAliveTime,:SiloName,:Hostname,:Address,:Port,:Generation,:StartTime,:Status,:ProxyPort,:Version) FROM DUAL 186 | '); 187 | / 188 | 189 | INSERT INTO OrleansQuery(QueryKey, QueryText) 190 | VALUES 191 | ( 192 | 'UpdateMembershipKey',' 193 | SELECT UpdateMembership(:DeploymentId, :Address, :Port, :Generation, :IAmAliveTime, :Status, :SuspectTimes, :Version) AS RESULT FROM DUAL 194 | '); 195 | / 196 | 197 | INSERT INTO OrleansQuery(QueryKey, QueryText) 198 | VALUES 199 | ( 200 | 'MembershipReadRowKey',' 201 | SELECT v.DeploymentId, m.Address, m.Port, m.Generation, m.SiloName, m.HostName, 202 | m.Status, m.ProxyPort, m.SuspectTimes, m.StartTime, m.IAmAliveTime, v.Version 203 | FROM 204 | OrleansMembershipVersionTable v 205 | LEFT OUTER JOIN OrleansMembershipTable m ON v.DeploymentId = m.DeploymentId 206 | AND Address = :Address AND :Address IS NOT NULL 207 | AND Port = :Port AND :Port IS NOT NULL 208 | AND Generation = :Generation AND :Generation IS NOT NULL 209 | WHERE 210 | v.DeploymentId = :DeploymentId AND :DeploymentId IS NOT NULL 211 | '); 212 | / 213 | 214 | INSERT INTO OrleansQuery(QueryKey, QueryText) 215 | VALUES 216 | ( 217 | 'MembershipReadAllKey',' 218 | SELECT v.DeploymentId, m.Address, m.Port, m.Generation, m.SiloName, m.HostName, m.Status, 219 | m.ProxyPort, m.SuspectTimes, m.StartTime, m.IAmAliveTime, v.Version 220 | FROM 221 | OrleansMembershipVersionTable v 222 | LEFT OUTER JOIN OrleansMembershipTable m ON v.DeploymentId = m.DeploymentId 223 | WHERE 224 | v.DeploymentId = :DeploymentId AND :DeploymentId IS NOT NULL 225 | '); 226 | / 227 | 228 | INSERT INTO OrleansQuery(QueryKey, QueryText) 229 | VALUES 230 | ( 231 | 'DeleteMembershipTableEntriesKey',' 232 | BEGIN 233 | DELETE FROM OrleansMembershipTable 234 | WHERE DeploymentId = :DeploymentId AND :DeploymentId IS NOT NULL; 235 | DELETE FROM OrleansMembershipVersionTable 236 | WHERE DeploymentId = :DeploymentId AND :DeploymentId IS NOT NULL; 237 | END; 238 | '); 239 | / 240 | 241 | INSERT INTO OrleansQuery(QueryKey, QueryText) 242 | VALUES 243 | ( 244 | 'GatewaysQueryKey',' 245 | SELECT Address, ProxyPort, Generation 246 | FROM OrleansMembershipTable 247 | WHERE DeploymentId = :DeploymentId AND :DeploymentId IS NOT NULL 248 | AND Status = :Status AND :Status IS NOT NULL 249 | AND ProxyPort > 0 250 | '); 251 | / 252 | 253 | COMMIT; 254 | -------------------------------------------------------------------------------- /AppClient/OrleansAdoNetContent/Oracle/Oracle-Main.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation notes: 3 | 4 | 1) The general idea is that data is read and written through Orleans specific queries. 5 | Orleans operates on column names and types when reading and on parameter names and types when writing. 6 | 7 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 8 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 9 | is maintained. 10 | 11 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 12 | by virtue of uniform naming across concrete implementations. 13 | 14 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 15 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 16 | 17 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 18 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 19 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 20 | Orleans handles exception as a failure and will retry. 21 | 22 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 23 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Runtime-Tables.html 24 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Cluster-Management 25 | https://github.com/dotnet/orleans/blob/master/src/Orleans/SystemTargetInterfaces/IMembershipTable.cs 26 | */ 27 | 28 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 29 | -- these are the only queries Orleans issues to the database. 30 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 31 | CREATE TABLE "ORLEANSQUERY" 32 | ( 33 | "QUERYKEY" VARCHAR2(64 BYTE) NOT NULL ENABLE, 34 | "QUERYTEXT" VARCHAR2(4000 BYTE), 35 | 36 | CONSTRAINT "ORLEANSQUERY_PK" PRIMARY KEY ("QUERYKEY") 37 | ); 38 | / 39 | 40 | COMMIT; 41 | 42 | -- Oracle specific implementation note: 43 | -- Some OrleansQueries are implemented as functions and differ from the scripts of other databases. 44 | -- The main reason for this is the fact, that oracle doesn't support returning variables from queries 45 | -- directly. So in the case that a variable value is needed as output of a OrleansQuery (e.g. version) 46 | -- a function is used. 47 | -------------------------------------------------------------------------------- /AppClient/OrleansAdoNetContent/PostgreSQL/PostgreSQL-Main.sql: -------------------------------------------------------------------------------- 1 | -- requires Postgres 9.5 (or perhaps higher) 2 | 3 | /* 4 | Implementation notes: 5 | 6 | 1) The general idea is that data is read and written through Orleans specific queries. 7 | Orleans operates on column names and types when reading and on parameter names and types when writing. 8 | 9 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 10 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 11 | is maintained. 12 | 13 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 14 | by virtue of uniform naming across concrete implementations. 15 | 16 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 17 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 18 | 19 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 20 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 21 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 22 | Orleans handles exception as a failure and will retry. 23 | 24 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 25 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Runtime-Tables.html 26 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Cluster-Management 27 | https://github.com/dotnet/orleans/blob/master/src/Orleans/SystemTargetInterfaces/IMembershipTable.cs 28 | */ 29 | 30 | 31 | 32 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 33 | -- these are the only queries Orleans issues to the database. 34 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 35 | CREATE TABLE OrleansQuery 36 | ( 37 | QueryKey varchar(64) NOT NULL, 38 | QueryText varchar(8000) NOT NULL, 39 | 40 | CONSTRAINT OrleansQuery_Key PRIMARY KEY(QueryKey) 41 | ); 42 | -------------------------------------------------------------------------------- /AppClient/OrleansAdoNetContent/SQLServer/SQLServer-Clustering.sql: -------------------------------------------------------------------------------- 1 | -- For each deployment, there will be only one (active) membership version table version column which will be updated periodically. 2 | CREATE TABLE OrleansMembershipVersionTable 3 | ( 4 | DeploymentId NVARCHAR(150) NOT NULL, 5 | Timestamp DATETIME2(3) NOT NULL DEFAULT GETUTCDATE(), 6 | Version INT NOT NULL DEFAULT 0, 7 | 8 | CONSTRAINT PK_OrleansMembershipVersionTable_DeploymentId PRIMARY KEY(DeploymentId) 9 | ); 10 | 11 | -- Every silo instance has a row in the membership table. 12 | CREATE TABLE OrleansMembershipTable 13 | ( 14 | DeploymentId NVARCHAR(150) NOT NULL, 15 | Address VARCHAR(45) NOT NULL, 16 | Port INT NOT NULL, 17 | Generation INT NOT NULL, 18 | SiloName NVARCHAR(150) NOT NULL, 19 | HostName NVARCHAR(150) NOT NULL, 20 | Status INT NOT NULL, 21 | ProxyPort INT NULL, 22 | SuspectTimes VARCHAR(8000) NULL, 23 | StartTime DATETIME2(3) NOT NULL, 24 | IAmAliveTime DATETIME2(3) NOT NULL, 25 | 26 | CONSTRAINT PK_MembershipTable_DeploymentId PRIMARY KEY(DeploymentId, Address, Port, Generation), 27 | CONSTRAINT FK_MembershipTable_MembershipVersionTable_DeploymentId FOREIGN KEY (DeploymentId) REFERENCES OrleansMembershipVersionTable (DeploymentId) 28 | ); 29 | 30 | INSERT INTO OrleansQuery(QueryKey, QueryText) 31 | VALUES 32 | ( 33 | 'UpdateIAmAlivetimeKey',' 34 | -- This is expected to never fail by Orleans, so return value 35 | -- is not needed nor is it checked. 36 | SET NOCOUNT ON; 37 | UPDATE OrleansMembershipTable 38 | SET 39 | IAmAliveTime = @IAmAliveTime 40 | WHERE 41 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 42 | AND Address = @Address AND @Address IS NOT NULL 43 | AND Port = @Port AND @Port IS NOT NULL 44 | AND Generation = @Generation AND @Generation IS NOT NULL; 45 | '); 46 | 47 | INSERT INTO OrleansQuery(QueryKey, QueryText) 48 | VALUES 49 | ( 50 | 'InsertMembershipVersionKey',' 51 | SET NOCOUNT ON; 52 | INSERT INTO OrleansMembershipVersionTable 53 | ( 54 | DeploymentId 55 | ) 56 | SELECT @DeploymentId 57 | WHERE NOT EXISTS 58 | ( 59 | SELECT 1 60 | FROM 61 | OrleansMembershipVersionTable 62 | WHERE 63 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 64 | ); 65 | 66 | SELECT @@ROWCOUNT; 67 | '); 68 | 69 | INSERT INTO OrleansQuery(QueryKey, QueryText) 70 | VALUES 71 | ( 72 | 'InsertMembershipKey',' 73 | SET XACT_ABORT, NOCOUNT ON; 74 | DECLARE @ROWCOUNT AS INT; 75 | BEGIN TRANSACTION; 76 | INSERT INTO OrleansMembershipTable 77 | ( 78 | DeploymentId, 79 | Address, 80 | Port, 81 | Generation, 82 | SiloName, 83 | HostName, 84 | Status, 85 | ProxyPort, 86 | StartTime, 87 | IAmAliveTime 88 | ) 89 | SELECT 90 | @DeploymentId, 91 | @Address, 92 | @Port, 93 | @Generation, 94 | @SiloName, 95 | @HostName, 96 | @Status, 97 | @ProxyPort, 98 | @StartTime, 99 | @IAmAliveTime 100 | WHERE NOT EXISTS 101 | ( 102 | SELECT 1 103 | FROM 104 | OrleansMembershipTable 105 | WHERE 106 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 107 | AND Address = @Address AND @Address IS NOT NULL 108 | AND Port = @Port AND @Port IS NOT NULL 109 | AND Generation = @Generation AND @Generation IS NOT NULL 110 | ); 111 | 112 | UPDATE OrleansMembershipVersionTable 113 | SET 114 | Timestamp = GETUTCDATE(), 115 | Version = Version + 1 116 | WHERE 117 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 118 | AND Version = @Version AND @Version IS NOT NULL 119 | AND @@ROWCOUNT > 0; 120 | 121 | SET @ROWCOUNT = @@ROWCOUNT; 122 | 123 | IF @ROWCOUNT = 0 124 | ROLLBACK TRANSACTION 125 | ELSE 126 | COMMIT TRANSACTION 127 | SELECT @ROWCOUNT; 128 | '); 129 | 130 | INSERT INTO OrleansQuery(QueryKey, QueryText) 131 | VALUES 132 | ( 133 | 'UpdateMembershipKey',' 134 | SET XACT_ABORT, NOCOUNT ON; 135 | BEGIN TRANSACTION; 136 | 137 | UPDATE OrleansMembershipVersionTable 138 | SET 139 | Timestamp = GETUTCDATE(), 140 | Version = Version + 1 141 | WHERE 142 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 143 | AND Version = @Version AND @Version IS NOT NULL; 144 | 145 | UPDATE OrleansMembershipTable 146 | SET 147 | Status = @Status, 148 | SuspectTimes = @SuspectTimes, 149 | IAmAliveTime = @IAmAliveTime 150 | WHERE 151 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 152 | AND Address = @Address AND @Address IS NOT NULL 153 | AND Port = @Port AND @Port IS NOT NULL 154 | AND Generation = @Generation AND @Generation IS NOT NULL 155 | AND @@ROWCOUNT > 0; 156 | 157 | SELECT @@ROWCOUNT; 158 | COMMIT TRANSACTION; 159 | '); 160 | 161 | INSERT INTO OrleansQuery(QueryKey, QueryText) 162 | VALUES 163 | ( 164 | 'GatewaysQueryKey',' 165 | SELECT 166 | Address, 167 | ProxyPort, 168 | Generation 169 | FROM 170 | OrleansMembershipTable 171 | WHERE 172 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 173 | AND Status = @Status AND @Status IS NOT NULL 174 | AND ProxyPort > 0; 175 | '); 176 | 177 | INSERT INTO OrleansQuery(QueryKey, QueryText) 178 | VALUES 179 | ( 180 | 'MembershipReadRowKey',' 181 | SELECT 182 | v.DeploymentId, 183 | m.Address, 184 | m.Port, 185 | m.Generation, 186 | m.SiloName, 187 | m.HostName, 188 | m.Status, 189 | m.ProxyPort, 190 | m.SuspectTimes, 191 | m.StartTime, 192 | m.IAmAliveTime, 193 | v.Version 194 | FROM 195 | OrleansMembershipVersionTable v 196 | -- This ensures the version table will returned even if there is no matching membership row. 197 | LEFT OUTER JOIN OrleansMembershipTable m ON v.DeploymentId = m.DeploymentId 198 | AND Address = @Address AND @Address IS NOT NULL 199 | AND Port = @Port AND @Port IS NOT NULL 200 | AND Generation = @Generation AND @Generation IS NOT NULL 201 | WHERE 202 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 203 | '); 204 | 205 | INSERT INTO OrleansQuery(QueryKey, QueryText) 206 | VALUES 207 | ( 208 | 'MembershipReadAllKey',' 209 | SELECT 210 | v.DeploymentId, 211 | m.Address, 212 | m.Port, 213 | m.Generation, 214 | m.SiloName, 215 | m.HostName, 216 | m.Status, 217 | m.ProxyPort, 218 | m.SuspectTimes, 219 | m.StartTime, 220 | m.IAmAliveTime, 221 | v.Version 222 | FROM 223 | OrleansMembershipVersionTable v LEFT OUTER JOIN OrleansMembershipTable m 224 | ON v.DeploymentId = m.DeploymentId 225 | WHERE 226 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 227 | '); 228 | 229 | INSERT INTO OrleansQuery(QueryKey, QueryText) 230 | VALUES 231 | ( 232 | 'DeleteMembershipTableEntriesKey',' 233 | DELETE FROM OrleansMembershipTable 234 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 235 | DELETE FROM OrleansMembershipVersionTable 236 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 237 | '); 238 | -------------------------------------------------------------------------------- /AppClient/OrleansAdoNetContent/SQLServer/SQLServer-Main.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation notes: 3 | 4 | 1) The general idea is that data is read and written through Orleans specific queries. 5 | Orleans operates on column names and types when reading and on parameter names and types when writing. 6 | 7 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 8 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 9 | is maintained. 10 | 11 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 12 | by virtue of uniform naming across concrete implementations. 13 | 14 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 15 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 16 | 17 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 18 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 19 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 20 | Orleans handles exception as a failure and will retry. 21 | 22 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 23 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Runtime-Tables.html 24 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Cluster-Management 25 | https://github.com/dotnet/orleans/blob/master/src/Orleans/SystemTargetInterfaces/IMembershipTable.cs 26 | */ 27 | 28 | -- These settings improves throughput of the database by reducing locking by better separating readers from writers. 29 | -- SQL Server 2012 and newer can refer to itself as CURRENT. Older ones need a workaround. 30 | DECLARE @current NVARCHAR(256); 31 | DECLARE @snapshotSettings NVARCHAR(612); 32 | 33 | SELECT @current = (SELECT DB_NAME()); 34 | SET @snapshotSettings = N'ALTER DATABASE ' + @current + N' SET READ_COMMITTED_SNAPSHOT ON; ALTER DATABASE ' + @current + N' SET ALLOW_SNAPSHOT_ISOLATION ON;'; 35 | 36 | EXECUTE sp_executesql @snapshotSettings; 37 | 38 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 39 | -- these are the only queries Orleans issues to the database. 40 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 41 | CREATE TABLE OrleansQuery 42 | ( 43 | QueryKey VARCHAR(64) NOT NULL, 44 | QueryText VARCHAR(8000) NOT NULL, 45 | 46 | CONSTRAINT OrleansQuery_Key PRIMARY KEY(QueryKey) 47 | ); 48 | -------------------------------------------------------------------------------- /AppClient/Program.cs: -------------------------------------------------------------------------------- 1 | using Demo.IGrain; 2 | using Microsoft.Extensions.Logging; 3 | using Orleans; 4 | using Orleans.Configuration; 5 | using System; 6 | using System.Threading.Tasks; 7 | using Orleans.Runtime; 8 | 9 | namespace AppClient 10 | { 11 | public class Program 12 | { 13 | const int initializeAttemptsBeforeFailing = 5; 14 | private static int attempt = 0; 15 | 16 | static void Main(string[] args) 17 | { 18 | using (var client = StartClientWithRetries().GetAwaiter().GetResult()) 19 | { 20 | DoClientWork(client).GetAwaiter().GetResult(); 21 | Console.ReadKey(); 22 | } 23 | } 24 | private static async Task RetryFilter(Exception exception) 25 | { 26 | if (exception.GetType() != typeof(SiloUnavailableException)) 27 | { 28 | Console.WriteLine($"Cluster client failed to connect to cluster with unexpected error. Exception: {exception}"); 29 | return false; 30 | } 31 | attempt++; 32 | Console.WriteLine($"Cluster client attempt {attempt} of {initializeAttemptsBeforeFailing} failed to connect to cluster. Exception: {exception}"); 33 | if (attempt > initializeAttemptsBeforeFailing) 34 | { 35 | return false; 36 | } 37 | await Task.Delay(TimeSpan.FromSeconds(4)); 38 | return true; 39 | } 40 | private static async Task StartClientWithRetries() 41 | { 42 | attempt = 0; 43 | IClusterClient client; 44 | client = new ClientBuilder() 45 | .UseLocalhostClustering() 46 | .Configure(options => 47 | { 48 | options.ClusterId = "Demo"; 49 | options.ServiceId = "Demo"; 50 | }) 51 | .ConfigureLogging(logging => logging.AddConsole()) 52 | .Build(); 53 | await client.Connect(RetryFilter); 54 | Console.WriteLine("Client successfully connect to silo host"); 55 | return client; 56 | } 57 | 58 | private static async Task DoClientWork(IClusterClient client) 59 | { 60 | var friend = client.GetGrain(0); 61 | var response = await friend.SayHello("Console,Good morning, my friend!"); 62 | Console.WriteLine("\n\n{0}\n\n", response); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /AppCloud/AppCloud.sfproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 97e5974d-ed2f-47ae-9d05-7af5601f6004 6 | 2.2 7 | 1.5 8 | 1.6.6 9 | v4.7.1 10 | 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Service Fabric Tools\Microsoft.VisualStudio.Azure.Fabric.ApplicationProject.targets 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /AppCloud/ApplicationPackageRoot/ApplicationManifest.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /AppCloud/ApplicationParameters/Cloud.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /AppCloud/ApplicationParameters/Local.1Node.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AppCloud/ApplicationParameters/Local.5Node.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AppCloud/PublishProfiles/Cloud.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /AppCloud/PublishProfiles/Local.1Node.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /AppCloud/PublishProfiles/Local.5Node.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /AppCloud/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /AppHost/AppHost.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /AppHost/OrleansAdoNetContent/MySQL/MySQL-Clustering.sql: -------------------------------------------------------------------------------- 1 | -- For each deployment, there will be only one (active) membership version table version column which will be updated periodically. 2 | CREATE TABLE OrleansMembershipVersionTable 3 | ( 4 | DeploymentId NVARCHAR(150) NOT NULL, 5 | Timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 6 | Version INT NOT NULL DEFAULT 0, 7 | 8 | CONSTRAINT PK_OrleansMembershipVersionTable_DeploymentId PRIMARY KEY(DeploymentId) 9 | ); 10 | 11 | -- Every silo instance has a row in the membership table. 12 | CREATE TABLE OrleansMembershipTable 13 | ( 14 | DeploymentId NVARCHAR(150) NOT NULL, 15 | Address VARCHAR(45) NOT NULL, 16 | Port INT NOT NULL, 17 | Generation INT NOT NULL, 18 | SiloName NVARCHAR(150) NOT NULL, 19 | HostName NVARCHAR(150) NOT NULL, 20 | Status INT NOT NULL, 21 | ProxyPort INT NULL, 22 | SuspectTimes VARCHAR(8000) NULL, 23 | StartTime DATETIME NOT NULL, 24 | IAmAliveTime DATETIME NOT NULL, 25 | 26 | CONSTRAINT PK_MembershipTable_DeploymentId PRIMARY KEY(DeploymentId, Address, Port, Generation), 27 | CONSTRAINT FK_MembershipTable_MembershipVersionTable_DeploymentId FOREIGN KEY (DeploymentId) REFERENCES OrleansMembershipVersionTable (DeploymentId) 28 | ); 29 | 30 | INSERT INTO OrleansQuery(QueryKey, QueryText) 31 | VALUES 32 | ( 33 | 'UpdateIAmAlivetimeKey',' 34 | -- This is expected to never fail by Orleans, so return value 35 | -- is not needed nor is it checked. 36 | UPDATE OrleansMembershipTable 37 | SET 38 | IAmAliveTime = @IAmAliveTime 39 | WHERE 40 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 41 | AND Address = @Address AND @Address IS NOT NULL 42 | AND Port = @Port AND @Port IS NOT NULL 43 | AND Generation = @Generation AND @Generation IS NOT NULL; 44 | '); 45 | 46 | INSERT INTO OrleansQuery(QueryKey, QueryText) 47 | VALUES 48 | ( 49 | 'InsertMembershipVersionKey',' 50 | INSERT INTO OrleansMembershipVersionTable 51 | ( 52 | DeploymentId 53 | ) 54 | SELECT * FROM ( SELECT @DeploymentId ) AS TMP 55 | WHERE NOT EXISTS 56 | ( 57 | SELECT 1 58 | FROM 59 | OrleansMembershipVersionTable 60 | WHERE 61 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 62 | ); 63 | 64 | SELECT ROW_COUNT(); 65 | '); 66 | 67 | INSERT INTO OrleansQuery(QueryKey, QueryText) 68 | VALUES 69 | ( 70 | 'InsertMembershipKey',' 71 | call InsertMembershipKey(@DeploymentId, @Address, @Port, @Generation, 72 | @Version, @SiloName, @HostName, @Status, @ProxyPort, @StartTime, @IAmAliveTime);' 73 | ); 74 | 75 | DELIMITER $$ 76 | 77 | CREATE PROCEDURE InsertMembershipKey( 78 | in _DeploymentId NVARCHAR(150), 79 | in _Address VARCHAR(45), 80 | in _Port INT, 81 | in _Generation INT, 82 | in _Version INT, 83 | in _SiloName NVARCHAR(150), 84 | in _HostName NVARCHAR(150), 85 | in _Status INT, 86 | in _ProxyPort INT, 87 | in _StartTime DATETIME, 88 | in _IAmAliveTime DATETIME 89 | ) 90 | BEGIN 91 | DECLARE _ROWCOUNT INT; 92 | START TRANSACTION; 93 | INSERT INTO OrleansMembershipTable 94 | ( 95 | DeploymentId, 96 | Address, 97 | Port, 98 | Generation, 99 | SiloName, 100 | HostName, 101 | Status, 102 | ProxyPort, 103 | StartTime, 104 | IAmAliveTime 105 | ) 106 | SELECT * FROM ( SELECT 107 | _DeploymentId, 108 | _Address, 109 | _Port, 110 | _Generation, 111 | _SiloName, 112 | _HostName, 113 | _Status, 114 | _ProxyPort, 115 | _StartTime, 116 | _IAmAliveTime) AS TMP 117 | WHERE NOT EXISTS 118 | ( 119 | SELECT 1 120 | FROM 121 | OrleansMembershipTable 122 | WHERE 123 | DeploymentId = _DeploymentId AND _DeploymentId IS NOT NULL 124 | AND Address = _Address AND _Address IS NOT NULL 125 | AND Port = _Port AND _Port IS NOT NULL 126 | AND Generation = _Generation AND _Generation IS NOT NULL 127 | ); 128 | 129 | UPDATE OrleansMembershipVersionTable 130 | SET 131 | Version = Version + 1 132 | WHERE 133 | DeploymentId = _DeploymentId AND _DeploymentId IS NOT NULL 134 | AND Version = _Version AND _Version IS NOT NULL 135 | AND ROW_COUNT() > 0; 136 | 137 | SET _ROWCOUNT = ROW_COUNT(); 138 | 139 | IF _ROWCOUNT = 0 140 | THEN 141 | ROLLBACK; 142 | ELSE 143 | COMMIT; 144 | END IF; 145 | SELECT _ROWCOUNT; 146 | END$$ 147 | 148 | DELIMITER ; 149 | 150 | INSERT INTO OrleansQuery(QueryKey, QueryText) 151 | VALUES 152 | ( 153 | 'UpdateMembershipKey',' 154 | START TRANSACTION; 155 | 156 | UPDATE OrleansMembershipVersionTable 157 | SET 158 | Version = Version + 1 159 | WHERE 160 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 161 | AND Version = @Version AND @Version IS NOT NULL; 162 | 163 | UPDATE OrleansMembershipTable 164 | SET 165 | Status = @Status, 166 | SuspectTimes = @SuspectTimes, 167 | IAmAliveTime = @IAmAliveTime 168 | WHERE 169 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 170 | AND Address = @Address AND @Address IS NOT NULL 171 | AND Port = @Port AND @Port IS NOT NULL 172 | AND Generation = @Generation AND @Generation IS NOT NULL 173 | AND ROW_COUNT() > 0; 174 | 175 | SELECT ROW_COUNT(); 176 | COMMIT; 177 | '); 178 | 179 | INSERT INTO OrleansQuery(QueryKey, QueryText) 180 | VALUES 181 | ( 182 | 'GatewaysQueryKey',' 183 | SELECT 184 | Address, 185 | ProxyPort, 186 | Generation 187 | FROM 188 | OrleansMembershipTable 189 | WHERE 190 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 191 | AND Status = @Status AND @Status IS NOT NULL 192 | AND ProxyPort > 0; 193 | '); 194 | 195 | INSERT INTO OrleansQuery(QueryKey, QueryText) 196 | VALUES 197 | ( 198 | 'MembershipReadRowKey',' 199 | SELECT 200 | v.DeploymentId, 201 | m.Address, 202 | m.Port, 203 | m.Generation, 204 | m.SiloName, 205 | m.HostName, 206 | m.Status, 207 | m.ProxyPort, 208 | m.SuspectTimes, 209 | m.StartTime, 210 | m.IAmAliveTime, 211 | v.Version 212 | FROM 213 | OrleansMembershipVersionTable v 214 | -- This ensures the version table will returned even if there is no matching membership row. 215 | LEFT OUTER JOIN OrleansMembershipTable m ON v.DeploymentId = m.DeploymentId 216 | AND Address = @Address AND @Address IS NOT NULL 217 | AND Port = @Port AND @Port IS NOT NULL 218 | AND Generation = @Generation AND @Generation IS NOT NULL 219 | WHERE 220 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 221 | '); 222 | 223 | INSERT INTO OrleansQuery(QueryKey, QueryText) 224 | VALUES 225 | ( 226 | 'MembershipReadAllKey',' 227 | SELECT 228 | v.DeploymentId, 229 | m.Address, 230 | m.Port, 231 | m.Generation, 232 | m.SiloName, 233 | m.HostName, 234 | m.Status, 235 | m.ProxyPort, 236 | m.SuspectTimes, 237 | m.StartTime, 238 | m.IAmAliveTime, 239 | v.Version 240 | FROM 241 | OrleansMembershipVersionTable v LEFT OUTER JOIN OrleansMembershipTable m 242 | ON v.DeploymentId = m.DeploymentId 243 | WHERE 244 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 245 | '); 246 | 247 | INSERT INTO OrleansQuery(QueryKey, QueryText) 248 | VALUES 249 | ( 250 | 'DeleteMembershipTableEntriesKey',' 251 | DELETE FROM OrleansMembershipTable 252 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 253 | DELETE FROM OrleansMembershipVersionTable 254 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 255 | '); 256 | -------------------------------------------------------------------------------- /AppHost/OrleansAdoNetContent/MySQL/MySQL-Main.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation notes: 3 | 4 | 1) The general idea is that data is read and written through Orleans specific queries. 5 | Orleans operates on column names and types when reading and on parameter names and types when writing. 6 | 7 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 8 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 9 | is maintained. 10 | 11 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 12 | by virtue of uniform naming across concrete implementations. 13 | 14 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 15 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 16 | 17 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 18 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 19 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 20 | Orleans handles exception as a failure and will retry. 21 | 22 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 23 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Runtime-Tables.html 24 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Cluster-Management 25 | https://github.com/dotnet/orleans/blob/master/src/Orleans/SystemTargetInterfaces/IMembershipTable.cs 26 | */ 27 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 28 | -- these are the only queries Orleans issues to the database. 29 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 30 | CREATE TABLE OrleansQuery 31 | ( 32 | QueryKey VARCHAR(64) NOT NULL, 33 | QueryText VARCHAR(8000) NOT NULL, 34 | 35 | CONSTRAINT OrleansQuery_Key PRIMARY KEY(QueryKey) 36 | ); 37 | -------------------------------------------------------------------------------- /AppHost/OrleansAdoNetContent/Oracle/Oracle-Main.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation notes: 3 | 4 | 1) The general idea is that data is read and written through Orleans specific queries. 5 | Orleans operates on column names and types when reading and on parameter names and types when writing. 6 | 7 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 8 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 9 | is maintained. 10 | 11 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 12 | by virtue of uniform naming across concrete implementations. 13 | 14 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 15 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 16 | 17 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 18 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 19 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 20 | Orleans handles exception as a failure and will retry. 21 | 22 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 23 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Runtime-Tables.html 24 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Cluster-Management 25 | https://github.com/dotnet/orleans/blob/master/src/Orleans/SystemTargetInterfaces/IMembershipTable.cs 26 | */ 27 | 28 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 29 | -- these are the only queries Orleans issues to the database. 30 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 31 | CREATE TABLE "ORLEANSQUERY" 32 | ( 33 | "QUERYKEY" VARCHAR2(64 BYTE) NOT NULL ENABLE, 34 | "QUERYTEXT" VARCHAR2(4000 BYTE), 35 | 36 | CONSTRAINT "ORLEANSQUERY_PK" PRIMARY KEY ("QUERYKEY") 37 | ); 38 | / 39 | 40 | COMMIT; 41 | 42 | -- Oracle specific implementation note: 43 | -- Some OrleansQueries are implemented as functions and differ from the scripts of other databases. 44 | -- The main reason for this is the fact, that oracle doesn't support returning variables from queries 45 | -- directly. So in the case that a variable value is needed as output of a OrleansQuery (e.g. version) 46 | -- a function is used. 47 | -------------------------------------------------------------------------------- /AppHost/OrleansAdoNetContent/PostgreSQL/PostgreSQL-Main.sql: -------------------------------------------------------------------------------- 1 | -- requires Postgres 9.5 (or perhaps higher) 2 | 3 | /* 4 | Implementation notes: 5 | 6 | 1) The general idea is that data is read and written through Orleans specific queries. 7 | Orleans operates on column names and types when reading and on parameter names and types when writing. 8 | 9 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 10 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 11 | is maintained. 12 | 13 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 14 | by virtue of uniform naming across concrete implementations. 15 | 16 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 17 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 18 | 19 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 20 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 21 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 22 | Orleans handles exception as a failure and will retry. 23 | 24 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 25 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Runtime-Tables.html 26 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Cluster-Management 27 | https://github.com/dotnet/orleans/blob/master/src/Orleans/SystemTargetInterfaces/IMembershipTable.cs 28 | */ 29 | 30 | 31 | 32 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 33 | -- these are the only queries Orleans issues to the database. 34 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 35 | CREATE TABLE OrleansQuery 36 | ( 37 | QueryKey varchar(64) NOT NULL, 38 | QueryText varchar(8000) NOT NULL, 39 | 40 | CONSTRAINT OrleansQuery_Key PRIMARY KEY(QueryKey) 41 | ); 42 | -------------------------------------------------------------------------------- /AppHost/OrleansAdoNetContent/SQLServer/SQLServer-Clustering.sql: -------------------------------------------------------------------------------- 1 | -- For each deployment, there will be only one (active) membership version table version column which will be updated periodically. 2 | CREATE TABLE OrleansMembershipVersionTable 3 | ( 4 | DeploymentId NVARCHAR(150) NOT NULL, 5 | Timestamp DATETIME2(3) NOT NULL DEFAULT GETUTCDATE(), 6 | Version INT NOT NULL DEFAULT 0, 7 | 8 | CONSTRAINT PK_OrleansMembershipVersionTable_DeploymentId PRIMARY KEY(DeploymentId) 9 | ); 10 | 11 | -- Every silo instance has a row in the membership table. 12 | CREATE TABLE OrleansMembershipTable 13 | ( 14 | DeploymentId NVARCHAR(150) NOT NULL, 15 | Address VARCHAR(45) NOT NULL, 16 | Port INT NOT NULL, 17 | Generation INT NOT NULL, 18 | SiloName NVARCHAR(150) NOT NULL, 19 | HostName NVARCHAR(150) NOT NULL, 20 | Status INT NOT NULL, 21 | ProxyPort INT NULL, 22 | SuspectTimes VARCHAR(8000) NULL, 23 | StartTime DATETIME2(3) NOT NULL, 24 | IAmAliveTime DATETIME2(3) NOT NULL, 25 | 26 | CONSTRAINT PK_MembershipTable_DeploymentId PRIMARY KEY(DeploymentId, Address, Port, Generation), 27 | CONSTRAINT FK_MembershipTable_MembershipVersionTable_DeploymentId FOREIGN KEY (DeploymentId) REFERENCES OrleansMembershipVersionTable (DeploymentId) 28 | ); 29 | 30 | INSERT INTO OrleansQuery(QueryKey, QueryText) 31 | VALUES 32 | ( 33 | 'UpdateIAmAlivetimeKey',' 34 | -- This is expected to never fail by Orleans, so return value 35 | -- is not needed nor is it checked. 36 | SET NOCOUNT ON; 37 | UPDATE OrleansMembershipTable 38 | SET 39 | IAmAliveTime = @IAmAliveTime 40 | WHERE 41 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 42 | AND Address = @Address AND @Address IS NOT NULL 43 | AND Port = @Port AND @Port IS NOT NULL 44 | AND Generation = @Generation AND @Generation IS NOT NULL; 45 | '); 46 | 47 | INSERT INTO OrleansQuery(QueryKey, QueryText) 48 | VALUES 49 | ( 50 | 'InsertMembershipVersionKey',' 51 | SET NOCOUNT ON; 52 | INSERT INTO OrleansMembershipVersionTable 53 | ( 54 | DeploymentId 55 | ) 56 | SELECT @DeploymentId 57 | WHERE NOT EXISTS 58 | ( 59 | SELECT 1 60 | FROM 61 | OrleansMembershipVersionTable 62 | WHERE 63 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 64 | ); 65 | 66 | SELECT @@ROWCOUNT; 67 | '); 68 | 69 | INSERT INTO OrleansQuery(QueryKey, QueryText) 70 | VALUES 71 | ( 72 | 'InsertMembershipKey',' 73 | SET XACT_ABORT, NOCOUNT ON; 74 | DECLARE @ROWCOUNT AS INT; 75 | BEGIN TRANSACTION; 76 | INSERT INTO OrleansMembershipTable 77 | ( 78 | DeploymentId, 79 | Address, 80 | Port, 81 | Generation, 82 | SiloName, 83 | HostName, 84 | Status, 85 | ProxyPort, 86 | StartTime, 87 | IAmAliveTime 88 | ) 89 | SELECT 90 | @DeploymentId, 91 | @Address, 92 | @Port, 93 | @Generation, 94 | @SiloName, 95 | @HostName, 96 | @Status, 97 | @ProxyPort, 98 | @StartTime, 99 | @IAmAliveTime 100 | WHERE NOT EXISTS 101 | ( 102 | SELECT 1 103 | FROM 104 | OrleansMembershipTable 105 | WHERE 106 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 107 | AND Address = @Address AND @Address IS NOT NULL 108 | AND Port = @Port AND @Port IS NOT NULL 109 | AND Generation = @Generation AND @Generation IS NOT NULL 110 | ); 111 | 112 | UPDATE OrleansMembershipVersionTable 113 | SET 114 | Timestamp = GETUTCDATE(), 115 | Version = Version + 1 116 | WHERE 117 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 118 | AND Version = @Version AND @Version IS NOT NULL 119 | AND @@ROWCOUNT > 0; 120 | 121 | SET @ROWCOUNT = @@ROWCOUNT; 122 | 123 | IF @ROWCOUNT = 0 124 | ROLLBACK TRANSACTION 125 | ELSE 126 | COMMIT TRANSACTION 127 | SELECT @ROWCOUNT; 128 | '); 129 | 130 | INSERT INTO OrleansQuery(QueryKey, QueryText) 131 | VALUES 132 | ( 133 | 'UpdateMembershipKey',' 134 | SET XACT_ABORT, NOCOUNT ON; 135 | BEGIN TRANSACTION; 136 | 137 | UPDATE OrleansMembershipVersionTable 138 | SET 139 | Timestamp = GETUTCDATE(), 140 | Version = Version + 1 141 | WHERE 142 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 143 | AND Version = @Version AND @Version IS NOT NULL; 144 | 145 | UPDATE OrleansMembershipTable 146 | SET 147 | Status = @Status, 148 | SuspectTimes = @SuspectTimes, 149 | IAmAliveTime = @IAmAliveTime 150 | WHERE 151 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 152 | AND Address = @Address AND @Address IS NOT NULL 153 | AND Port = @Port AND @Port IS NOT NULL 154 | AND Generation = @Generation AND @Generation IS NOT NULL 155 | AND @@ROWCOUNT > 0; 156 | 157 | SELECT @@ROWCOUNT; 158 | COMMIT TRANSACTION; 159 | '); 160 | 161 | INSERT INTO OrleansQuery(QueryKey, QueryText) 162 | VALUES 163 | ( 164 | 'GatewaysQueryKey',' 165 | SELECT 166 | Address, 167 | ProxyPort, 168 | Generation 169 | FROM 170 | OrleansMembershipTable 171 | WHERE 172 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 173 | AND Status = @Status AND @Status IS NOT NULL 174 | AND ProxyPort > 0; 175 | '); 176 | 177 | INSERT INTO OrleansQuery(QueryKey, QueryText) 178 | VALUES 179 | ( 180 | 'MembershipReadRowKey',' 181 | SELECT 182 | v.DeploymentId, 183 | m.Address, 184 | m.Port, 185 | m.Generation, 186 | m.SiloName, 187 | m.HostName, 188 | m.Status, 189 | m.ProxyPort, 190 | m.SuspectTimes, 191 | m.StartTime, 192 | m.IAmAliveTime, 193 | v.Version 194 | FROM 195 | OrleansMembershipVersionTable v 196 | -- This ensures the version table will returned even if there is no matching membership row. 197 | LEFT OUTER JOIN OrleansMembershipTable m ON v.DeploymentId = m.DeploymentId 198 | AND Address = @Address AND @Address IS NOT NULL 199 | AND Port = @Port AND @Port IS NOT NULL 200 | AND Generation = @Generation AND @Generation IS NOT NULL 201 | WHERE 202 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 203 | '); 204 | 205 | INSERT INTO OrleansQuery(QueryKey, QueryText) 206 | VALUES 207 | ( 208 | 'MembershipReadAllKey',' 209 | SELECT 210 | v.DeploymentId, 211 | m.Address, 212 | m.Port, 213 | m.Generation, 214 | m.SiloName, 215 | m.HostName, 216 | m.Status, 217 | m.ProxyPort, 218 | m.SuspectTimes, 219 | m.StartTime, 220 | m.IAmAliveTime, 221 | v.Version 222 | FROM 223 | OrleansMembershipVersionTable v LEFT OUTER JOIN OrleansMembershipTable m 224 | ON v.DeploymentId = m.DeploymentId 225 | WHERE 226 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 227 | '); 228 | 229 | INSERT INTO OrleansQuery(QueryKey, QueryText) 230 | VALUES 231 | ( 232 | 'DeleteMembershipTableEntriesKey',' 233 | DELETE FROM OrleansMembershipTable 234 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 235 | DELETE FROM OrleansMembershipVersionTable 236 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 237 | '); 238 | -------------------------------------------------------------------------------- /AppHost/OrleansAdoNetContent/SQLServer/SQLServer-Main.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation notes: 3 | 4 | 1) The general idea is that data is read and written through Orleans specific queries. 5 | Orleans operates on column names and types when reading and on parameter names and types when writing. 6 | 7 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 8 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 9 | is maintained. 10 | 11 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 12 | by virtue of uniform naming across concrete implementations. 13 | 14 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 15 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 16 | 17 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 18 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 19 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 20 | Orleans handles exception as a failure and will retry. 21 | 22 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 23 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Runtime-Tables.html 24 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Cluster-Management 25 | https://github.com/dotnet/orleans/blob/master/src/Orleans/SystemTargetInterfaces/IMembershipTable.cs 26 | */ 27 | 28 | -- These settings improves throughput of the database by reducing locking by better separating readers from writers. 29 | -- SQL Server 2012 and newer can refer to itself as CURRENT. Older ones need a workaround. 30 | DECLARE @current NVARCHAR(256); 31 | DECLARE @snapshotSettings NVARCHAR(612); 32 | 33 | SELECT @current = (SELECT DB_NAME()); 34 | SET @snapshotSettings = N'ALTER DATABASE ' + @current + N' SET READ_COMMITTED_SNAPSHOT ON; ALTER DATABASE ' + @current + N' SET ALLOW_SNAPSHOT_ISOLATION ON;'; 35 | 36 | EXECUTE sp_executesql @snapshotSettings; 37 | 38 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 39 | -- these are the only queries Orleans issues to the database. 40 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 41 | CREATE TABLE OrleansQuery 42 | ( 43 | QueryKey VARCHAR(64) NOT NULL, 44 | QueryText VARCHAR(8000) NOT NULL, 45 | 46 | CONSTRAINT OrleansQuery_Key PRIMARY KEY(QueryKey) 47 | ); 48 | -------------------------------------------------------------------------------- /AppHost/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading.Tasks; 4 | using Demo.Grain; 5 | using Microsoft.Extensions.Logging; 6 | using Orleans; 7 | using Orleans.Configuration; 8 | using Orleans.Hosting; 9 | 10 | namespace AppHost 11 | { 12 | class Program 13 | { 14 | static void Main(string[] args) 15 | { 16 | var host = StartSilo().Result; 17 | Console.WriteLine("Press Enter to terminate..."); 18 | Console.ReadLine(); 19 | } 20 | private static async Task StartSilo() 21 | { 22 | var invariant = "Npgsql"; 23 | const string connectionString = "Server=10.0.2.63;Port=5432;Database=Orleans;User Id=postgres;Password=the19800isbest;Pooling=false;"; 24 | var builder = new SiloHostBuilder() 25 | #if DEBUG 26 | .UseLocalhostClustering() 27 | #else 28 | .UseAdoNetClustering(options => 29 | { 30 | options.ConnectionString = connectionString; 31 | options.Invariant = invariant; 32 | }) 33 | #endif 34 | .Configure(options => 35 | { 36 | options.ClusterId = "Demo"; 37 | options.ServiceId = "Demo"; 38 | }) 39 | .ConfigureApplicationParts(parts => parts.AddApplicationPart(typeof(HelloGrain).Assembly).WithReferences()) 40 | .ConfigureLogging(logging => logging.AddConsole()); 41 | var host = builder.Build(); 42 | await host.StartAsync(); 43 | return host; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /AppWebClient/AppWebClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /AppWebClient/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Demo.IGrain; 4 | using Lib; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.Extensions.Logging; 7 | using Orleans; 8 | using Orleans.Configuration; 9 | using Orleans.Runtime; 10 | 11 | namespace AppWebClient.Controllers 12 | { 13 | public class HomeController : Controller 14 | { 15 | private IClientFactory clientFactory; 16 | public HomeController(IClientFactory clientFactory) 17 | { 18 | this.clientFactory = clientFactory; 19 | } 20 | public async Task Index() 21 | { 22 | var client = clientFactory.GetClient(); 23 | var actor = client.GetGrain(0); 24 | var r = await actor.SayHello("Kiwi"); 25 | return Content(r); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /AppWebClient/OrleansAdoNetContent/MySQL/MySQL-Clustering.sql: -------------------------------------------------------------------------------- 1 | -- For each deployment, there will be only one (active) membership version table version column which will be updated periodically. 2 | CREATE TABLE OrleansMembershipVersionTable 3 | ( 4 | DeploymentId NVARCHAR(150) NOT NULL, 5 | Timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 6 | Version INT NOT NULL DEFAULT 0, 7 | 8 | CONSTRAINT PK_OrleansMembershipVersionTable_DeploymentId PRIMARY KEY(DeploymentId) 9 | ); 10 | 11 | -- Every silo instance has a row in the membership table. 12 | CREATE TABLE OrleansMembershipTable 13 | ( 14 | DeploymentId NVARCHAR(150) NOT NULL, 15 | Address VARCHAR(45) NOT NULL, 16 | Port INT NOT NULL, 17 | Generation INT NOT NULL, 18 | SiloName NVARCHAR(150) NOT NULL, 19 | HostName NVARCHAR(150) NOT NULL, 20 | Status INT NOT NULL, 21 | ProxyPort INT NULL, 22 | SuspectTimes VARCHAR(8000) NULL, 23 | StartTime DATETIME NOT NULL, 24 | IAmAliveTime DATETIME NOT NULL, 25 | 26 | CONSTRAINT PK_MembershipTable_DeploymentId PRIMARY KEY(DeploymentId, Address, Port, Generation), 27 | CONSTRAINT FK_MembershipTable_MembershipVersionTable_DeploymentId FOREIGN KEY (DeploymentId) REFERENCES OrleansMembershipVersionTable (DeploymentId) 28 | ); 29 | 30 | INSERT INTO OrleansQuery(QueryKey, QueryText) 31 | VALUES 32 | ( 33 | 'UpdateIAmAlivetimeKey',' 34 | -- This is expected to never fail by Orleans, so return value 35 | -- is not needed nor is it checked. 36 | UPDATE OrleansMembershipTable 37 | SET 38 | IAmAliveTime = @IAmAliveTime 39 | WHERE 40 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 41 | AND Address = @Address AND @Address IS NOT NULL 42 | AND Port = @Port AND @Port IS NOT NULL 43 | AND Generation = @Generation AND @Generation IS NOT NULL; 44 | '); 45 | 46 | INSERT INTO OrleansQuery(QueryKey, QueryText) 47 | VALUES 48 | ( 49 | 'InsertMembershipVersionKey',' 50 | INSERT INTO OrleansMembershipVersionTable 51 | ( 52 | DeploymentId 53 | ) 54 | SELECT * FROM ( SELECT @DeploymentId ) AS TMP 55 | WHERE NOT EXISTS 56 | ( 57 | SELECT 1 58 | FROM 59 | OrleansMembershipVersionTable 60 | WHERE 61 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 62 | ); 63 | 64 | SELECT ROW_COUNT(); 65 | '); 66 | 67 | INSERT INTO OrleansQuery(QueryKey, QueryText) 68 | VALUES 69 | ( 70 | 'InsertMembershipKey',' 71 | call InsertMembershipKey(@DeploymentId, @Address, @Port, @Generation, 72 | @Version, @SiloName, @HostName, @Status, @ProxyPort, @StartTime, @IAmAliveTime);' 73 | ); 74 | 75 | DELIMITER $$ 76 | 77 | CREATE PROCEDURE InsertMembershipKey( 78 | in _DeploymentId NVARCHAR(150), 79 | in _Address VARCHAR(45), 80 | in _Port INT, 81 | in _Generation INT, 82 | in _Version INT, 83 | in _SiloName NVARCHAR(150), 84 | in _HostName NVARCHAR(150), 85 | in _Status INT, 86 | in _ProxyPort INT, 87 | in _StartTime DATETIME, 88 | in _IAmAliveTime DATETIME 89 | ) 90 | BEGIN 91 | DECLARE _ROWCOUNT INT; 92 | START TRANSACTION; 93 | INSERT INTO OrleansMembershipTable 94 | ( 95 | DeploymentId, 96 | Address, 97 | Port, 98 | Generation, 99 | SiloName, 100 | HostName, 101 | Status, 102 | ProxyPort, 103 | StartTime, 104 | IAmAliveTime 105 | ) 106 | SELECT * FROM ( SELECT 107 | _DeploymentId, 108 | _Address, 109 | _Port, 110 | _Generation, 111 | _SiloName, 112 | _HostName, 113 | _Status, 114 | _ProxyPort, 115 | _StartTime, 116 | _IAmAliveTime) AS TMP 117 | WHERE NOT EXISTS 118 | ( 119 | SELECT 1 120 | FROM 121 | OrleansMembershipTable 122 | WHERE 123 | DeploymentId = _DeploymentId AND _DeploymentId IS NOT NULL 124 | AND Address = _Address AND _Address IS NOT NULL 125 | AND Port = _Port AND _Port IS NOT NULL 126 | AND Generation = _Generation AND _Generation IS NOT NULL 127 | ); 128 | 129 | UPDATE OrleansMembershipVersionTable 130 | SET 131 | Version = Version + 1 132 | WHERE 133 | DeploymentId = _DeploymentId AND _DeploymentId IS NOT NULL 134 | AND Version = _Version AND _Version IS NOT NULL 135 | AND ROW_COUNT() > 0; 136 | 137 | SET _ROWCOUNT = ROW_COUNT(); 138 | 139 | IF _ROWCOUNT = 0 140 | THEN 141 | ROLLBACK; 142 | ELSE 143 | COMMIT; 144 | END IF; 145 | SELECT _ROWCOUNT; 146 | END$$ 147 | 148 | DELIMITER ; 149 | 150 | INSERT INTO OrleansQuery(QueryKey, QueryText) 151 | VALUES 152 | ( 153 | 'UpdateMembershipKey',' 154 | START TRANSACTION; 155 | 156 | UPDATE OrleansMembershipVersionTable 157 | SET 158 | Version = Version + 1 159 | WHERE 160 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 161 | AND Version = @Version AND @Version IS NOT NULL; 162 | 163 | UPDATE OrleansMembershipTable 164 | SET 165 | Status = @Status, 166 | SuspectTimes = @SuspectTimes, 167 | IAmAliveTime = @IAmAliveTime 168 | WHERE 169 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 170 | AND Address = @Address AND @Address IS NOT NULL 171 | AND Port = @Port AND @Port IS NOT NULL 172 | AND Generation = @Generation AND @Generation IS NOT NULL 173 | AND ROW_COUNT() > 0; 174 | 175 | SELECT ROW_COUNT(); 176 | COMMIT; 177 | '); 178 | 179 | INSERT INTO OrleansQuery(QueryKey, QueryText) 180 | VALUES 181 | ( 182 | 'GatewaysQueryKey',' 183 | SELECT 184 | Address, 185 | ProxyPort, 186 | Generation 187 | FROM 188 | OrleansMembershipTable 189 | WHERE 190 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 191 | AND Status = @Status AND @Status IS NOT NULL 192 | AND ProxyPort > 0; 193 | '); 194 | 195 | INSERT INTO OrleansQuery(QueryKey, QueryText) 196 | VALUES 197 | ( 198 | 'MembershipReadRowKey',' 199 | SELECT 200 | v.DeploymentId, 201 | m.Address, 202 | m.Port, 203 | m.Generation, 204 | m.SiloName, 205 | m.HostName, 206 | m.Status, 207 | m.ProxyPort, 208 | m.SuspectTimes, 209 | m.StartTime, 210 | m.IAmAliveTime, 211 | v.Version 212 | FROM 213 | OrleansMembershipVersionTable v 214 | -- This ensures the version table will returned even if there is no matching membership row. 215 | LEFT OUTER JOIN OrleansMembershipTable m ON v.DeploymentId = m.DeploymentId 216 | AND Address = @Address AND @Address IS NOT NULL 217 | AND Port = @Port AND @Port IS NOT NULL 218 | AND Generation = @Generation AND @Generation IS NOT NULL 219 | WHERE 220 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 221 | '); 222 | 223 | INSERT INTO OrleansQuery(QueryKey, QueryText) 224 | VALUES 225 | ( 226 | 'MembershipReadAllKey',' 227 | SELECT 228 | v.DeploymentId, 229 | m.Address, 230 | m.Port, 231 | m.Generation, 232 | m.SiloName, 233 | m.HostName, 234 | m.Status, 235 | m.ProxyPort, 236 | m.SuspectTimes, 237 | m.StartTime, 238 | m.IAmAliveTime, 239 | v.Version 240 | FROM 241 | OrleansMembershipVersionTable v LEFT OUTER JOIN OrleansMembershipTable m 242 | ON v.DeploymentId = m.DeploymentId 243 | WHERE 244 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 245 | '); 246 | 247 | INSERT INTO OrleansQuery(QueryKey, QueryText) 248 | VALUES 249 | ( 250 | 'DeleteMembershipTableEntriesKey',' 251 | DELETE FROM OrleansMembershipTable 252 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 253 | DELETE FROM OrleansMembershipVersionTable 254 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 255 | '); 256 | -------------------------------------------------------------------------------- /AppWebClient/OrleansAdoNetContent/MySQL/MySQL-Main.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation notes: 3 | 4 | 1) The general idea is that data is read and written through Orleans specific queries. 5 | Orleans operates on column names and types when reading and on parameter names and types when writing. 6 | 7 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 8 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 9 | is maintained. 10 | 11 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 12 | by virtue of uniform naming across concrete implementations. 13 | 14 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 15 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 16 | 17 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 18 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 19 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 20 | Orleans handles exception as a failure and will retry. 21 | 22 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 23 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Runtime-Tables.html 24 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Cluster-Management 25 | https://github.com/dotnet/orleans/blob/master/src/Orleans/SystemTargetInterfaces/IMembershipTable.cs 26 | */ 27 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 28 | -- these are the only queries Orleans issues to the database. 29 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 30 | CREATE TABLE OrleansQuery 31 | ( 32 | QueryKey VARCHAR(64) NOT NULL, 33 | QueryText VARCHAR(8000) NOT NULL, 34 | 35 | CONSTRAINT OrleansQuery_Key PRIMARY KEY(QueryKey) 36 | ); 37 | -------------------------------------------------------------------------------- /AppWebClient/OrleansAdoNetContent/Oracle/Oracle-Main.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation notes: 3 | 4 | 1) The general idea is that data is read and written through Orleans specific queries. 5 | Orleans operates on column names and types when reading and on parameter names and types when writing. 6 | 7 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 8 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 9 | is maintained. 10 | 11 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 12 | by virtue of uniform naming across concrete implementations. 13 | 14 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 15 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 16 | 17 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 18 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 19 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 20 | Orleans handles exception as a failure and will retry. 21 | 22 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 23 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Runtime-Tables.html 24 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Cluster-Management 25 | https://github.com/dotnet/orleans/blob/master/src/Orleans/SystemTargetInterfaces/IMembershipTable.cs 26 | */ 27 | 28 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 29 | -- these are the only queries Orleans issues to the database. 30 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 31 | CREATE TABLE "ORLEANSQUERY" 32 | ( 33 | "QUERYKEY" VARCHAR2(64 BYTE) NOT NULL ENABLE, 34 | "QUERYTEXT" VARCHAR2(4000 BYTE), 35 | 36 | CONSTRAINT "ORLEANSQUERY_PK" PRIMARY KEY ("QUERYKEY") 37 | ); 38 | / 39 | 40 | COMMIT; 41 | 42 | -- Oracle specific implementation note: 43 | -- Some OrleansQueries are implemented as functions and differ from the scripts of other databases. 44 | -- The main reason for this is the fact, that oracle doesn't support returning variables from queries 45 | -- directly. So in the case that a variable value is needed as output of a OrleansQuery (e.g. version) 46 | -- a function is used. 47 | -------------------------------------------------------------------------------- /AppWebClient/OrleansAdoNetContent/PostgreSQL/PostgreSQL-Main.sql: -------------------------------------------------------------------------------- 1 | -- requires Postgres 9.5 (or perhaps higher) 2 | 3 | /* 4 | Implementation notes: 5 | 6 | 1) The general idea is that data is read and written through Orleans specific queries. 7 | Orleans operates on column names and types when reading and on parameter names and types when writing. 8 | 9 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 10 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 11 | is maintained. 12 | 13 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 14 | by virtue of uniform naming across concrete implementations. 15 | 16 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 17 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 18 | 19 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 20 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 21 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 22 | Orleans handles exception as a failure and will retry. 23 | 24 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 25 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Runtime-Tables.html 26 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Cluster-Management 27 | https://github.com/dotnet/orleans/blob/master/src/Orleans/SystemTargetInterfaces/IMembershipTable.cs 28 | */ 29 | 30 | 31 | 32 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 33 | -- these are the only queries Orleans issues to the database. 34 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 35 | CREATE TABLE OrleansQuery 36 | ( 37 | QueryKey varchar(64) NOT NULL, 38 | QueryText varchar(8000) NOT NULL, 39 | 40 | CONSTRAINT OrleansQuery_Key PRIMARY KEY(QueryKey) 41 | ); 42 | -------------------------------------------------------------------------------- /AppWebClient/OrleansAdoNetContent/SQLServer/SQLServer-Clustering.sql: -------------------------------------------------------------------------------- 1 | -- For each deployment, there will be only one (active) membership version table version column which will be updated periodically. 2 | CREATE TABLE OrleansMembershipVersionTable 3 | ( 4 | DeploymentId NVARCHAR(150) NOT NULL, 5 | Timestamp DATETIME2(3) NOT NULL DEFAULT GETUTCDATE(), 6 | Version INT NOT NULL DEFAULT 0, 7 | 8 | CONSTRAINT PK_OrleansMembershipVersionTable_DeploymentId PRIMARY KEY(DeploymentId) 9 | ); 10 | 11 | -- Every silo instance has a row in the membership table. 12 | CREATE TABLE OrleansMembershipTable 13 | ( 14 | DeploymentId NVARCHAR(150) NOT NULL, 15 | Address VARCHAR(45) NOT NULL, 16 | Port INT NOT NULL, 17 | Generation INT NOT NULL, 18 | SiloName NVARCHAR(150) NOT NULL, 19 | HostName NVARCHAR(150) NOT NULL, 20 | Status INT NOT NULL, 21 | ProxyPort INT NULL, 22 | SuspectTimes VARCHAR(8000) NULL, 23 | StartTime DATETIME2(3) NOT NULL, 24 | IAmAliveTime DATETIME2(3) NOT NULL, 25 | 26 | CONSTRAINT PK_MembershipTable_DeploymentId PRIMARY KEY(DeploymentId, Address, Port, Generation), 27 | CONSTRAINT FK_MembershipTable_MembershipVersionTable_DeploymentId FOREIGN KEY (DeploymentId) REFERENCES OrleansMembershipVersionTable (DeploymentId) 28 | ); 29 | 30 | INSERT INTO OrleansQuery(QueryKey, QueryText) 31 | VALUES 32 | ( 33 | 'UpdateIAmAlivetimeKey',' 34 | -- This is expected to never fail by Orleans, so return value 35 | -- is not needed nor is it checked. 36 | SET NOCOUNT ON; 37 | UPDATE OrleansMembershipTable 38 | SET 39 | IAmAliveTime = @IAmAliveTime 40 | WHERE 41 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 42 | AND Address = @Address AND @Address IS NOT NULL 43 | AND Port = @Port AND @Port IS NOT NULL 44 | AND Generation = @Generation AND @Generation IS NOT NULL; 45 | '); 46 | 47 | INSERT INTO OrleansQuery(QueryKey, QueryText) 48 | VALUES 49 | ( 50 | 'InsertMembershipVersionKey',' 51 | SET NOCOUNT ON; 52 | INSERT INTO OrleansMembershipVersionTable 53 | ( 54 | DeploymentId 55 | ) 56 | SELECT @DeploymentId 57 | WHERE NOT EXISTS 58 | ( 59 | SELECT 1 60 | FROM 61 | OrleansMembershipVersionTable 62 | WHERE 63 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 64 | ); 65 | 66 | SELECT @@ROWCOUNT; 67 | '); 68 | 69 | INSERT INTO OrleansQuery(QueryKey, QueryText) 70 | VALUES 71 | ( 72 | 'InsertMembershipKey',' 73 | SET XACT_ABORT, NOCOUNT ON; 74 | DECLARE @ROWCOUNT AS INT; 75 | BEGIN TRANSACTION; 76 | INSERT INTO OrleansMembershipTable 77 | ( 78 | DeploymentId, 79 | Address, 80 | Port, 81 | Generation, 82 | SiloName, 83 | HostName, 84 | Status, 85 | ProxyPort, 86 | StartTime, 87 | IAmAliveTime 88 | ) 89 | SELECT 90 | @DeploymentId, 91 | @Address, 92 | @Port, 93 | @Generation, 94 | @SiloName, 95 | @HostName, 96 | @Status, 97 | @ProxyPort, 98 | @StartTime, 99 | @IAmAliveTime 100 | WHERE NOT EXISTS 101 | ( 102 | SELECT 1 103 | FROM 104 | OrleansMembershipTable 105 | WHERE 106 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 107 | AND Address = @Address AND @Address IS NOT NULL 108 | AND Port = @Port AND @Port IS NOT NULL 109 | AND Generation = @Generation AND @Generation IS NOT NULL 110 | ); 111 | 112 | UPDATE OrleansMembershipVersionTable 113 | SET 114 | Timestamp = GETUTCDATE(), 115 | Version = Version + 1 116 | WHERE 117 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 118 | AND Version = @Version AND @Version IS NOT NULL 119 | AND @@ROWCOUNT > 0; 120 | 121 | SET @ROWCOUNT = @@ROWCOUNT; 122 | 123 | IF @ROWCOUNT = 0 124 | ROLLBACK TRANSACTION 125 | ELSE 126 | COMMIT TRANSACTION 127 | SELECT @ROWCOUNT; 128 | '); 129 | 130 | INSERT INTO OrleansQuery(QueryKey, QueryText) 131 | VALUES 132 | ( 133 | 'UpdateMembershipKey',' 134 | SET XACT_ABORT, NOCOUNT ON; 135 | BEGIN TRANSACTION; 136 | 137 | UPDATE OrleansMembershipVersionTable 138 | SET 139 | Timestamp = GETUTCDATE(), 140 | Version = Version + 1 141 | WHERE 142 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 143 | AND Version = @Version AND @Version IS NOT NULL; 144 | 145 | UPDATE OrleansMembershipTable 146 | SET 147 | Status = @Status, 148 | SuspectTimes = @SuspectTimes, 149 | IAmAliveTime = @IAmAliveTime 150 | WHERE 151 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 152 | AND Address = @Address AND @Address IS NOT NULL 153 | AND Port = @Port AND @Port IS NOT NULL 154 | AND Generation = @Generation AND @Generation IS NOT NULL 155 | AND @@ROWCOUNT > 0; 156 | 157 | SELECT @@ROWCOUNT; 158 | COMMIT TRANSACTION; 159 | '); 160 | 161 | INSERT INTO OrleansQuery(QueryKey, QueryText) 162 | VALUES 163 | ( 164 | 'GatewaysQueryKey',' 165 | SELECT 166 | Address, 167 | ProxyPort, 168 | Generation 169 | FROM 170 | OrleansMembershipTable 171 | WHERE 172 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 173 | AND Status = @Status AND @Status IS NOT NULL 174 | AND ProxyPort > 0; 175 | '); 176 | 177 | INSERT INTO OrleansQuery(QueryKey, QueryText) 178 | VALUES 179 | ( 180 | 'MembershipReadRowKey',' 181 | SELECT 182 | v.DeploymentId, 183 | m.Address, 184 | m.Port, 185 | m.Generation, 186 | m.SiloName, 187 | m.HostName, 188 | m.Status, 189 | m.ProxyPort, 190 | m.SuspectTimes, 191 | m.StartTime, 192 | m.IAmAliveTime, 193 | v.Version 194 | FROM 195 | OrleansMembershipVersionTable v 196 | -- This ensures the version table will returned even if there is no matching membership row. 197 | LEFT OUTER JOIN OrleansMembershipTable m ON v.DeploymentId = m.DeploymentId 198 | AND Address = @Address AND @Address IS NOT NULL 199 | AND Port = @Port AND @Port IS NOT NULL 200 | AND Generation = @Generation AND @Generation IS NOT NULL 201 | WHERE 202 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 203 | '); 204 | 205 | INSERT INTO OrleansQuery(QueryKey, QueryText) 206 | VALUES 207 | ( 208 | 'MembershipReadAllKey',' 209 | SELECT 210 | v.DeploymentId, 211 | m.Address, 212 | m.Port, 213 | m.Generation, 214 | m.SiloName, 215 | m.HostName, 216 | m.Status, 217 | m.ProxyPort, 218 | m.SuspectTimes, 219 | m.StartTime, 220 | m.IAmAliveTime, 221 | v.Version 222 | FROM 223 | OrleansMembershipVersionTable v LEFT OUTER JOIN OrleansMembershipTable m 224 | ON v.DeploymentId = m.DeploymentId 225 | WHERE 226 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 227 | '); 228 | 229 | INSERT INTO OrleansQuery(QueryKey, QueryText) 230 | VALUES 231 | ( 232 | 'DeleteMembershipTableEntriesKey',' 233 | DELETE FROM OrleansMembershipTable 234 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 235 | DELETE FROM OrleansMembershipVersionTable 236 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 237 | '); 238 | -------------------------------------------------------------------------------- /AppWebClient/OrleansAdoNetContent/SQLServer/SQLServer-Main.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation notes: 3 | 4 | 1) The general idea is that data is read and written through Orleans specific queries. 5 | Orleans operates on column names and types when reading and on parameter names and types when writing. 6 | 7 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 8 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 9 | is maintained. 10 | 11 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 12 | by virtue of uniform naming across concrete implementations. 13 | 14 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 15 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 16 | 17 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 18 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 19 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 20 | Orleans handles exception as a failure and will retry. 21 | 22 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 23 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Runtime-Tables.html 24 | http://dotnet.github.io/orleans/Runtime-Implementation-Details/Cluster-Management 25 | https://github.com/dotnet/orleans/blob/master/src/Orleans/SystemTargetInterfaces/IMembershipTable.cs 26 | */ 27 | 28 | -- These settings improves throughput of the database by reducing locking by better separating readers from writers. 29 | -- SQL Server 2012 and newer can refer to itself as CURRENT. Older ones need a workaround. 30 | DECLARE @current NVARCHAR(256); 31 | DECLARE @snapshotSettings NVARCHAR(612); 32 | 33 | SELECT @current = (SELECT DB_NAME()); 34 | SET @snapshotSettings = N'ALTER DATABASE ' + @current + N' SET READ_COMMITTED_SNAPSHOT ON; ALTER DATABASE ' + @current + N' SET ALLOW_SNAPSHOT_ISOLATION ON;'; 35 | 36 | EXECUTE sp_executesql @snapshotSettings; 37 | 38 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 39 | -- these are the only queries Orleans issues to the database. 40 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 41 | CREATE TABLE OrleansQuery 42 | ( 43 | QueryKey VARCHAR(64) NOT NULL, 44 | QueryText VARCHAR(8000) NOT NULL, 45 | 46 | CONSTRAINT OrleansQuery_Key PRIMARY KEY(QueryKey) 47 | ); 48 | -------------------------------------------------------------------------------- /AppWebClient/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Demo.IGrain; 4 | using Lib; 5 | using Microsoft.AspNetCore; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Logging; 9 | using Orleans; 10 | using Orleans.Runtime; 11 | 12 | namespace AppWebClient 13 | { 14 | public class Program 15 | { 16 | public static void Main(string[] args) 17 | { 18 | StartClientWithRetries().GetAwaiter().GetResult(); 19 | BuildWebHost(args).Run(); 20 | } 21 | 22 | public static IWebHost BuildWebHost(string[] args) => 23 | WebHost.CreateDefaultBuilder(args) 24 | .UseStartup() 25 | .Build(); 26 | private static async Task StartClientWithRetries(int initializeAttemptsBeforeFailing = 5) 27 | { 28 | int attempt = 0; 29 | IClusterClient client; 30 | while (true) 31 | { 32 | try 33 | { 34 | client = await ClientFactory.Build(() => 35 | { 36 | var builder = new ClientBuilder() 37 | .UseLocalhostClustering() 38 | .ConfigureApplicationParts(parts => parts.AddApplicationPart(typeof(IHello).Assembly).WithReferences()) 39 | .ConfigureLogging(logging => logging.AddConsole()); 40 | return builder; 41 | }); 42 | Console.WriteLine("Client successfully connect to silo host"); 43 | break; 44 | } 45 | catch (SiloUnavailableException) 46 | { 47 | attempt++; 48 | Console.WriteLine($"Attempt {attempt} of {initializeAttemptsBeforeFailing} failed to initialize the Orleans client."); 49 | if (attempt > initializeAttemptsBeforeFailing) 50 | { 51 | throw; 52 | } 53 | await Task.Delay(TimeSpan.FromSeconds(4)); 54 | } 55 | } 56 | 57 | return client; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /AppWebClient/Startup.cs: -------------------------------------------------------------------------------- 1 | using Lib; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace AppWebClient 8 | { 9 | public class Startup 10 | { 11 | public Startup(IConfiguration configuration) 12 | { 13 | Configuration = configuration; 14 | } 15 | 16 | public IConfiguration Configuration { get; } 17 | 18 | // This method gets called by the runtime. Use this method to add services to the container. 19 | public void ConfigureServices(IServiceCollection services) 20 | { 21 | services.AddMvc(); 22 | services.AddSingleton(); 23 | } 24 | 25 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 26 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 27 | { 28 | if (env.IsDevelopment()) 29 | { 30 | app.UseBrowserLink(); 31 | app.UseDeveloperExceptionPage(); 32 | } 33 | else 34 | { 35 | app.UseExceptionHandler("/Home/Error"); 36 | } 37 | app.UseMvc(routes => 38 | { 39 | routes.MapRoute( 40 | name: "default", 41 | template: "{controller=Home}/{action=Index}/{id?}"); 42 | }); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /AppWebClient/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /AppWebClient/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Demo.Grain/Demo.Grain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Demo.Grain/HelloGrain .cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Demo.IGrain; 4 | using Microsoft.Extensions.Logging; 5 | 6 | namespace Demo.Grain 7 | { 8 | public class HelloGrain : Orleans.Grain, IHello 9 | { 10 | private readonly ILogger logger; 11 | private static string tempData; 12 | public HelloGrain(ILogger logger) 13 | { 14 | this.logger = logger; 15 | } 16 | Task IHello.SayHello(string greeting) 17 | { 18 | var result = string.IsNullOrEmpty(tempData) ? $"You said: '{greeting}', I say: Hello!" : $"You said:'{tempData}-{greeting}'"; 19 | return Task.FromResult(result); 20 | } 21 | 22 | public Task SetValue(string temp) 23 | { 24 | tempData = temp; 25 | return Task.CompletedTask; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Demo.IGrain/Demo.IGrain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Demo.IGrain/IHello.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Demo.IGrain 5 | { 6 | public interface IHello : Orleans.IGrainWithIntegerKey 7 | { 8 | Task SayHello(string greeting); 9 | Task SetValue(string greeting); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Demo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2047 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cloud", "Cloud", "{1A3410A2-B1C3-4AA8-91E2-1BF2A54D9756}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "App", "App", "{2A106D1F-7910-43B9-B1EE-78445BEFBD06}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demo.IGrain", "Demo.IGrain\Demo.IGrain.csproj", "{F80627F4-9158-40A3-98F9-126CFE495318}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demo.Grain", "Demo.Grain\Demo.Grain.csproj", "{894AD723-BADE-4BF9-939C-17B981D57EC1}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lib", "Lib\Lib.csproj", "{1E45E03D-AE5B-489D-B426-0A5B1E71116C}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppHost", "AppHost\AppHost.csproj", "{679979DD-A64F-4BAE-8AEE-052DCFB26863}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppClient", "AppClient\AppClient.csproj", "{0BC25862-9340-4C3E-993E-47E8EFDB75F0}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppWebClient", "AppWebClient\AppWebClient.csproj", "{E9ED88B6-2818-4F41-AA62-4907C03C6886}" 21 | EndProject 22 | Project("{A07B5EB6-E848-4116-A8D0-A826331D98C6}") = "AppCloud", "AppCloud\AppCloud.sfproj", "{97E5974D-ED2F-47AE-9D05-7AF5601F6004}" 23 | EndProject 24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StatelessHost", "StatelessHost\StatelessHost.csproj", "{C2CACFCA-5FEB-4BD1-BAB2-7ED1A332D184}" 25 | EndProject 26 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StatelessWebGo", "StatelessWebGo\StatelessWebGo.csproj", "{D2B9B82B-C07C-41C8-BB09-1558B21F0037}" 27 | EndProject 28 | Global 29 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 30 | Debug|Any CPU = Debug|Any CPU 31 | Debug|x64 = Debug|x64 32 | Release|Any CPU = Release|Any CPU 33 | Release|x64 = Release|x64 34 | EndGlobalSection 35 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 36 | {F80627F4-9158-40A3-98F9-126CFE495318}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {F80627F4-9158-40A3-98F9-126CFE495318}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {F80627F4-9158-40A3-98F9-126CFE495318}.Debug|x64.ActiveCfg = Debug|Any CPU 39 | {F80627F4-9158-40A3-98F9-126CFE495318}.Debug|x64.Build.0 = Debug|Any CPU 40 | {F80627F4-9158-40A3-98F9-126CFE495318}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {F80627F4-9158-40A3-98F9-126CFE495318}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {F80627F4-9158-40A3-98F9-126CFE495318}.Release|x64.ActiveCfg = Release|Any CPU 43 | {F80627F4-9158-40A3-98F9-126CFE495318}.Release|x64.Build.0 = Release|Any CPU 44 | {894AD723-BADE-4BF9-939C-17B981D57EC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {894AD723-BADE-4BF9-939C-17B981D57EC1}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {894AD723-BADE-4BF9-939C-17B981D57EC1}.Debug|x64.ActiveCfg = Debug|Any CPU 47 | {894AD723-BADE-4BF9-939C-17B981D57EC1}.Debug|x64.Build.0 = Debug|Any CPU 48 | {894AD723-BADE-4BF9-939C-17B981D57EC1}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {894AD723-BADE-4BF9-939C-17B981D57EC1}.Release|Any CPU.Build.0 = Release|Any CPU 50 | {894AD723-BADE-4BF9-939C-17B981D57EC1}.Release|x64.ActiveCfg = Release|Any CPU 51 | {894AD723-BADE-4BF9-939C-17B981D57EC1}.Release|x64.Build.0 = Release|Any CPU 52 | {1E45E03D-AE5B-489D-B426-0A5B1E71116C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 | {1E45E03D-AE5B-489D-B426-0A5B1E71116C}.Debug|Any CPU.Build.0 = Debug|Any CPU 54 | {1E45E03D-AE5B-489D-B426-0A5B1E71116C}.Debug|x64.ActiveCfg = Debug|Any CPU 55 | {1E45E03D-AE5B-489D-B426-0A5B1E71116C}.Debug|x64.Build.0 = Debug|Any CPU 56 | {1E45E03D-AE5B-489D-B426-0A5B1E71116C}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {1E45E03D-AE5B-489D-B426-0A5B1E71116C}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {1E45E03D-AE5B-489D-B426-0A5B1E71116C}.Release|x64.ActiveCfg = Release|Any CPU 59 | {1E45E03D-AE5B-489D-B426-0A5B1E71116C}.Release|x64.Build.0 = Release|Any CPU 60 | {679979DD-A64F-4BAE-8AEE-052DCFB26863}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 61 | {679979DD-A64F-4BAE-8AEE-052DCFB26863}.Debug|Any CPU.Build.0 = Debug|Any CPU 62 | {679979DD-A64F-4BAE-8AEE-052DCFB26863}.Debug|x64.ActiveCfg = Debug|Any CPU 63 | {679979DD-A64F-4BAE-8AEE-052DCFB26863}.Debug|x64.Build.0 = Debug|Any CPU 64 | {679979DD-A64F-4BAE-8AEE-052DCFB26863}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | {679979DD-A64F-4BAE-8AEE-052DCFB26863}.Release|Any CPU.Build.0 = Release|Any CPU 66 | {679979DD-A64F-4BAE-8AEE-052DCFB26863}.Release|x64.ActiveCfg = Release|Any CPU 67 | {679979DD-A64F-4BAE-8AEE-052DCFB26863}.Release|x64.Build.0 = Release|Any CPU 68 | {0BC25862-9340-4C3E-993E-47E8EFDB75F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 69 | {0BC25862-9340-4C3E-993E-47E8EFDB75F0}.Debug|Any CPU.Build.0 = Debug|Any CPU 70 | {0BC25862-9340-4C3E-993E-47E8EFDB75F0}.Debug|x64.ActiveCfg = Debug|Any CPU 71 | {0BC25862-9340-4C3E-993E-47E8EFDB75F0}.Debug|x64.Build.0 = Debug|Any CPU 72 | {0BC25862-9340-4C3E-993E-47E8EFDB75F0}.Release|Any CPU.ActiveCfg = Release|Any CPU 73 | {0BC25862-9340-4C3E-993E-47E8EFDB75F0}.Release|Any CPU.Build.0 = Release|Any CPU 74 | {0BC25862-9340-4C3E-993E-47E8EFDB75F0}.Release|x64.ActiveCfg = Release|Any CPU 75 | {0BC25862-9340-4C3E-993E-47E8EFDB75F0}.Release|x64.Build.0 = Release|Any CPU 76 | {E9ED88B6-2818-4F41-AA62-4907C03C6886}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 77 | {E9ED88B6-2818-4F41-AA62-4907C03C6886}.Debug|Any CPU.Build.0 = Debug|Any CPU 78 | {E9ED88B6-2818-4F41-AA62-4907C03C6886}.Debug|x64.ActiveCfg = Debug|Any CPU 79 | {E9ED88B6-2818-4F41-AA62-4907C03C6886}.Debug|x64.Build.0 = Debug|Any CPU 80 | {E9ED88B6-2818-4F41-AA62-4907C03C6886}.Release|Any CPU.ActiveCfg = Release|Any CPU 81 | {E9ED88B6-2818-4F41-AA62-4907C03C6886}.Release|Any CPU.Build.0 = Release|Any CPU 82 | {E9ED88B6-2818-4F41-AA62-4907C03C6886}.Release|x64.ActiveCfg = Release|Any CPU 83 | {E9ED88B6-2818-4F41-AA62-4907C03C6886}.Release|x64.Build.0 = Release|Any CPU 84 | {97E5974D-ED2F-47AE-9D05-7AF5601F6004}.Debug|Any CPU.ActiveCfg = Debug|x64 85 | {97E5974D-ED2F-47AE-9D05-7AF5601F6004}.Debug|x64.ActiveCfg = Debug|x64 86 | {97E5974D-ED2F-47AE-9D05-7AF5601F6004}.Debug|x64.Build.0 = Debug|x64 87 | {97E5974D-ED2F-47AE-9D05-7AF5601F6004}.Debug|x64.Deploy.0 = Debug|x64 88 | {97E5974D-ED2F-47AE-9D05-7AF5601F6004}.Release|Any CPU.ActiveCfg = Release|x64 89 | {97E5974D-ED2F-47AE-9D05-7AF5601F6004}.Release|x64.ActiveCfg = Release|x64 90 | {97E5974D-ED2F-47AE-9D05-7AF5601F6004}.Release|x64.Build.0 = Release|x64 91 | {97E5974D-ED2F-47AE-9D05-7AF5601F6004}.Release|x64.Deploy.0 = Release|x64 92 | {C2CACFCA-5FEB-4BD1-BAB2-7ED1A332D184}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 93 | {C2CACFCA-5FEB-4BD1-BAB2-7ED1A332D184}.Debug|Any CPU.Build.0 = Debug|Any CPU 94 | {C2CACFCA-5FEB-4BD1-BAB2-7ED1A332D184}.Debug|x64.ActiveCfg = Debug|Any CPU 95 | {C2CACFCA-5FEB-4BD1-BAB2-7ED1A332D184}.Debug|x64.Build.0 = Debug|Any CPU 96 | {C2CACFCA-5FEB-4BD1-BAB2-7ED1A332D184}.Release|Any CPU.ActiveCfg = Release|Any CPU 97 | {C2CACFCA-5FEB-4BD1-BAB2-7ED1A332D184}.Release|Any CPU.Build.0 = Release|Any CPU 98 | {C2CACFCA-5FEB-4BD1-BAB2-7ED1A332D184}.Release|x64.ActiveCfg = Release|Any CPU 99 | {C2CACFCA-5FEB-4BD1-BAB2-7ED1A332D184}.Release|x64.Build.0 = Release|Any CPU 100 | {D2B9B82B-C07C-41C8-BB09-1558B21F0037}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 101 | {D2B9B82B-C07C-41C8-BB09-1558B21F0037}.Debug|Any CPU.Build.0 = Debug|Any CPU 102 | {D2B9B82B-C07C-41C8-BB09-1558B21F0037}.Debug|x64.ActiveCfg = Debug|Any CPU 103 | {D2B9B82B-C07C-41C8-BB09-1558B21F0037}.Debug|x64.Build.0 = Debug|Any CPU 104 | {D2B9B82B-C07C-41C8-BB09-1558B21F0037}.Release|Any CPU.ActiveCfg = Release|Any CPU 105 | {D2B9B82B-C07C-41C8-BB09-1558B21F0037}.Release|Any CPU.Build.0 = Release|Any CPU 106 | {D2B9B82B-C07C-41C8-BB09-1558B21F0037}.Release|x64.ActiveCfg = Release|Any CPU 107 | {D2B9B82B-C07C-41C8-BB09-1558B21F0037}.Release|x64.Build.0 = Release|Any CPU 108 | EndGlobalSection 109 | GlobalSection(SolutionProperties) = preSolution 110 | HideSolutionNode = FALSE 111 | EndGlobalSection 112 | GlobalSection(NestedProjects) = preSolution 113 | {679979DD-A64F-4BAE-8AEE-052DCFB26863} = {2A106D1F-7910-43B9-B1EE-78445BEFBD06} 114 | {0BC25862-9340-4C3E-993E-47E8EFDB75F0} = {2A106D1F-7910-43B9-B1EE-78445BEFBD06} 115 | {E9ED88B6-2818-4F41-AA62-4907C03C6886} = {2A106D1F-7910-43B9-B1EE-78445BEFBD06} 116 | {97E5974D-ED2F-47AE-9D05-7AF5601F6004} = {1A3410A2-B1C3-4AA8-91E2-1BF2A54D9756} 117 | {C2CACFCA-5FEB-4BD1-BAB2-7ED1A332D184} = {1A3410A2-B1C3-4AA8-91E2-1BF2A54D9756} 118 | {D2B9B82B-C07C-41C8-BB09-1558B21F0037} = {1A3410A2-B1C3-4AA8-91E2-1BF2A54D9756} 119 | EndGlobalSection 120 | GlobalSection(ExtensibilityGlobals) = postSolution 121 | SolutionGuid = {7E0ECEC7-DD48-4333-BD31-40E8D780F3D7} 122 | EndGlobalSection 123 | EndGlobal 124 | -------------------------------------------------------------------------------- /Lib/ClientFactory.cs: -------------------------------------------------------------------------------- 1 | using Orleans; 2 | using System; 3 | using System.Threading.Tasks; 4 | 5 | namespace Lib 6 | { 7 | public class ClientFactory : IClientFactory 8 | { 9 | static Func _builderFunc; 10 | static IClusterClient _client; 11 | static bool needReBuild = false; 12 | public static async Task Build(Func builderFunc) 13 | { 14 | _builderFunc = builderFunc; 15 | _client = builderFunc().Build(); 16 | await _client.Connect(); 17 | return _client; 18 | } 19 | public static void ReBuild() 20 | { 21 | if (_client != null) 22 | { 23 | needReBuild = true; 24 | } 25 | } 26 | readonly object connectLock = new object(); 27 | public IClusterClient GetClient() 28 | { 29 | if (!_client.IsInitialized || needReBuild) 30 | { 31 | lock (connectLock) 32 | { 33 | if (!_client.IsInitialized || needReBuild) 34 | { 35 | if (needReBuild) 36 | { 37 | _client.Close(); 38 | _client.Dispose(); 39 | } 40 | _client = _builderFunc().Build(); 41 | _client.Connect().GetAwaiter().GetResult(); 42 | needReBuild = false; 43 | } 44 | } 45 | } 46 | return _client; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Lib/GlobalConfig.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Lib 7 | { 8 | /// 9 | /// A static class that groups together instances of IConfiguration and allows them to be accessed anywhere in the application 10 | /// 11 | public class GlobalConfig : ConfigurationProvider 12 | { 13 | private static Dictionary Sources { get; } = new Dictionary(); 14 | public static string ApplicationEnvironment { get; set; } 15 | 16 | /// 17 | /// Add an IConfiguration instance to the AppConfig object 18 | /// IConfiguration instance 19 | /// Name that can be used to refer to this instance 20 | /// 21 | public static void AddConfigurationObject(IConfiguration configuration, string sourceName) 22 | { 23 | if (Sources.ContainsKey(sourceName)) 24 | { 25 | throw new InvalidOperationException($"There is already a configuration source registered with the name {sourceName}"); 26 | } 27 | 28 | Sources.Add(sourceName, configuration); 29 | } 30 | 31 | /// 32 | /// Remove an IConfiguration instance from the AppConfig object 33 | /// Name of the IConfiguration instance to remove 34 | /// 35 | public static void RemoveConfigurationObject(string sourceName) 36 | { 37 | if (!Sources.ContainsKey(sourceName)) 38 | { 39 | throw new InvalidOperationException($"There is no configuration source registered with the name {sourceName}"); 40 | } 41 | 42 | Sources.Remove(sourceName); 43 | } 44 | 45 | /// 46 | /// Get the value of a specified key from a named IConfiguration instance 47 | /// Name of IConfiguration instance which to retrieve the key from 48 | /// Name of the key 49 | /// 50 | public static string Get(string sourceName, string key) 51 | { 52 | if (!Sources.ContainsKey(sourceName)) 53 | { 54 | throw new InvalidOperationException($"There is no configuration source registered with the name {sourceName}"); 55 | } 56 | 57 | return Sources[sourceName].GetSection(key).Value; 58 | } 59 | 60 | /// 61 | /// Set the value of a specified key in a named IConfiguration instance 62 | /// Name of IConfiguration instance where the key is to be set 63 | /// Name of the key 64 | /// Value to assign to the key 65 | /// 66 | public static void Set(string sourceName, string key, string value) 67 | { 68 | if (!Sources.ContainsKey(sourceName)) 69 | { 70 | throw new InvalidOperationException($"There is no configuration source registered with the name {sourceName}"); 71 | } 72 | 73 | Sources[sourceName].GetSection(key).Value = value; 74 | } 75 | 76 | /// 77 | /// Get all values from the specified configuration instance 78 | /// Name of IConfiguration instance where the key is to be set 79 | /// Returns a dictionary containing the configuration key-value pairs 80 | /// 81 | public static Dictionary GetAllValues(string sourceName) 82 | { 83 | if (!Sources.ContainsKey(sourceName)) 84 | { 85 | throw new InvalidOperationException("No configuration source registered with name " + sourceName); 86 | } 87 | 88 | // Get children of this source and return them 89 | IConfiguration sourceConfiguration = Sources[sourceName]; 90 | Dictionary valuesDictionary = RecurseConfig(sourceConfiguration); 91 | 92 | //Dictionary valuesDictionary = sourceConfiguration.GetChildren().ToDictionary(child => child.Key, child => child.Value); 93 | return valuesDictionary; 94 | } 95 | 96 | private static Dictionary RecurseConfig(IConfiguration source) 97 | { 98 | Dictionary result = new Dictionary(); 99 | 100 | foreach (var child in source.GetChildren()) 101 | { 102 | if (child.GetChildren().Count() != 0) 103 | { 104 | result = result.Concat(RecurseConfig(child)).GroupBy(d => d.Key).ToDictionary(d => d.Key, d => d.First().Value); 105 | } 106 | 107 | if (child.GetChildren().Count() != 0 && string.IsNullOrEmpty(child.Value)) 108 | { 109 | continue; 110 | } 111 | result.Add(child.Path, child.Value); 112 | } 113 | return result; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Lib/IClientFactory.cs: -------------------------------------------------------------------------------- 1 | using Orleans; 2 | 3 | namespace Lib 4 | { 5 | public interface IClientFactory 6 | { 7 | IClusterClient GetClient(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Lib/Lib.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OrleansSample 2 | 示例中集群关系用的PostgreSQL,可以在Docker中安装PostgreSQL 3 | ``` 4 | docker run -d -p 5432:5432 --name postgres -e POSTGRES_PASSWORD=123456 postgres 5 | ``` 6 | 7 | ![](https://note.youdao.com/yws/api/personal/file/0ECFBF7702FA4F418A4E5BD10EC2652F?method=download&shareKey=eb1097f72c05996410a4c400cfc1c860) 8 | 9 | ![](https://note.youdao.com/yws/api/personal/file/B73DC37502824B61BFFE7138AC930489?method=download&shareKey=4c4c50831eea48bb6b7a005bf9f8cd57) 10 | -------------------------------------------------------------------------------- /StatelessHost/OrleansAdoNetContent/MySQL/MySQL-Clustering.sql: -------------------------------------------------------------------------------- 1 | -- For each deployment, there will be only one (active) membership version table version column which will be updated periodically. 2 | CREATE TABLE OrleansMembershipVersionTable 3 | ( 4 | DeploymentId NVARCHAR(150) NOT NULL, 5 | Timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 6 | Version INT NOT NULL DEFAULT 0, 7 | 8 | CONSTRAINT PK_OrleansMembershipVersionTable_DeploymentId PRIMARY KEY(DeploymentId) 9 | ); 10 | 11 | -- Every silo instance has a row in the membership table. 12 | CREATE TABLE OrleansMembershipTable 13 | ( 14 | DeploymentId NVARCHAR(150) NOT NULL, 15 | Address VARCHAR(45) NOT NULL, 16 | Port INT NOT NULL, 17 | Generation INT NOT NULL, 18 | SiloName NVARCHAR(150) NOT NULL, 19 | HostName NVARCHAR(150) NOT NULL, 20 | Status INT NOT NULL, 21 | ProxyPort INT NULL, 22 | SuspectTimes VARCHAR(8000) NULL, 23 | StartTime DATETIME NOT NULL, 24 | IAmAliveTime DATETIME NOT NULL, 25 | 26 | CONSTRAINT PK_MembershipTable_DeploymentId PRIMARY KEY(DeploymentId, Address, Port, Generation), 27 | CONSTRAINT FK_MembershipTable_MembershipVersionTable_DeploymentId FOREIGN KEY (DeploymentId) REFERENCES OrleansMembershipVersionTable (DeploymentId) 28 | ); 29 | 30 | INSERT INTO OrleansQuery(QueryKey, QueryText) 31 | VALUES 32 | ( 33 | 'UpdateIAmAlivetimeKey',' 34 | -- This is expected to never fail by Orleans, so return value 35 | -- is not needed nor is it checked. 36 | UPDATE OrleansMembershipTable 37 | SET 38 | IAmAliveTime = @IAmAliveTime 39 | WHERE 40 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 41 | AND Address = @Address AND @Address IS NOT NULL 42 | AND Port = @Port AND @Port IS NOT NULL 43 | AND Generation = @Generation AND @Generation IS NOT NULL; 44 | '); 45 | 46 | INSERT INTO OrleansQuery(QueryKey, QueryText) 47 | VALUES 48 | ( 49 | 'InsertMembershipVersionKey',' 50 | INSERT INTO OrleansMembershipVersionTable 51 | ( 52 | DeploymentId 53 | ) 54 | SELECT * FROM ( SELECT @DeploymentId ) AS TMP 55 | WHERE NOT EXISTS 56 | ( 57 | SELECT 1 58 | FROM 59 | OrleansMembershipVersionTable 60 | WHERE 61 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 62 | ); 63 | 64 | SELECT ROW_COUNT(); 65 | '); 66 | 67 | INSERT INTO OrleansQuery(QueryKey, QueryText) 68 | VALUES 69 | ( 70 | 'InsertMembershipKey',' 71 | call InsertMembershipKey(@DeploymentId, @Address, @Port, @Generation, 72 | @Version, @SiloName, @HostName, @Status, @ProxyPort, @StartTime, @IAmAliveTime);' 73 | ); 74 | 75 | DELIMITER $$ 76 | 77 | CREATE PROCEDURE InsertMembershipKey( 78 | in _DeploymentId NVARCHAR(150), 79 | in _Address VARCHAR(45), 80 | in _Port INT, 81 | in _Generation INT, 82 | in _Version INT, 83 | in _SiloName NVARCHAR(150), 84 | in _HostName NVARCHAR(150), 85 | in _Status INT, 86 | in _ProxyPort INT, 87 | in _StartTime DATETIME, 88 | in _IAmAliveTime DATETIME 89 | ) 90 | BEGIN 91 | DECLARE _ROWCOUNT INT; 92 | START TRANSACTION; 93 | INSERT INTO OrleansMembershipTable 94 | ( 95 | DeploymentId, 96 | Address, 97 | Port, 98 | Generation, 99 | SiloName, 100 | HostName, 101 | Status, 102 | ProxyPort, 103 | StartTime, 104 | IAmAliveTime 105 | ) 106 | SELECT * FROM ( SELECT 107 | _DeploymentId, 108 | _Address, 109 | _Port, 110 | _Generation, 111 | _SiloName, 112 | _HostName, 113 | _Status, 114 | _ProxyPort, 115 | _StartTime, 116 | _IAmAliveTime) AS TMP 117 | WHERE NOT EXISTS 118 | ( 119 | SELECT 1 120 | FROM 121 | OrleansMembershipTable 122 | WHERE 123 | DeploymentId = _DeploymentId AND _DeploymentId IS NOT NULL 124 | AND Address = _Address AND _Address IS NOT NULL 125 | AND Port = _Port AND _Port IS NOT NULL 126 | AND Generation = _Generation AND _Generation IS NOT NULL 127 | ); 128 | 129 | UPDATE OrleansMembershipVersionTable 130 | SET 131 | Version = Version + 1 132 | WHERE 133 | DeploymentId = _DeploymentId AND _DeploymentId IS NOT NULL 134 | AND Version = _Version AND _Version IS NOT NULL 135 | AND ROW_COUNT() > 0; 136 | 137 | SET _ROWCOUNT = ROW_COUNT(); 138 | 139 | IF _ROWCOUNT = 0 140 | THEN 141 | ROLLBACK; 142 | ELSE 143 | COMMIT; 144 | END IF; 145 | SELECT _ROWCOUNT; 146 | END$$ 147 | 148 | DELIMITER ; 149 | 150 | INSERT INTO OrleansQuery(QueryKey, QueryText) 151 | VALUES 152 | ( 153 | 'UpdateMembershipKey',' 154 | START TRANSACTION; 155 | 156 | UPDATE OrleansMembershipVersionTable 157 | SET 158 | Version = Version + 1 159 | WHERE 160 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 161 | AND Version = @Version AND @Version IS NOT NULL; 162 | 163 | UPDATE OrleansMembershipTable 164 | SET 165 | Status = @Status, 166 | SuspectTimes = @SuspectTimes, 167 | IAmAliveTime = @IAmAliveTime 168 | WHERE 169 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 170 | AND Address = @Address AND @Address IS NOT NULL 171 | AND Port = @Port AND @Port IS NOT NULL 172 | AND Generation = @Generation AND @Generation IS NOT NULL 173 | AND ROW_COUNT() > 0; 174 | 175 | SELECT ROW_COUNT(); 176 | COMMIT; 177 | '); 178 | 179 | INSERT INTO OrleansQuery(QueryKey, QueryText) 180 | VALUES 181 | ( 182 | 'GatewaysQueryKey',' 183 | SELECT 184 | Address, 185 | ProxyPort, 186 | Generation 187 | FROM 188 | OrleansMembershipTable 189 | WHERE 190 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 191 | AND Status = @Status AND @Status IS NOT NULL 192 | AND ProxyPort > 0; 193 | '); 194 | 195 | INSERT INTO OrleansQuery(QueryKey, QueryText) 196 | VALUES 197 | ( 198 | 'MembershipReadRowKey',' 199 | SELECT 200 | v.DeploymentId, 201 | m.Address, 202 | m.Port, 203 | m.Generation, 204 | m.SiloName, 205 | m.HostName, 206 | m.Status, 207 | m.ProxyPort, 208 | m.SuspectTimes, 209 | m.StartTime, 210 | m.IAmAliveTime, 211 | v.Version 212 | FROM 213 | OrleansMembershipVersionTable v 214 | -- This ensures the version table will returned even if there is no matching membership row. 215 | LEFT OUTER JOIN OrleansMembershipTable m ON v.DeploymentId = m.DeploymentId 216 | AND Address = @Address AND @Address IS NOT NULL 217 | AND Port = @Port AND @Port IS NOT NULL 218 | AND Generation = @Generation AND @Generation IS NOT NULL 219 | WHERE 220 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 221 | '); 222 | 223 | INSERT INTO OrleansQuery(QueryKey, QueryText) 224 | VALUES 225 | ( 226 | 'MembershipReadAllKey',' 227 | SELECT 228 | v.DeploymentId, 229 | m.Address, 230 | m.Port, 231 | m.Generation, 232 | m.SiloName, 233 | m.HostName, 234 | m.Status, 235 | m.ProxyPort, 236 | m.SuspectTimes, 237 | m.StartTime, 238 | m.IAmAliveTime, 239 | v.Version 240 | FROM 241 | OrleansMembershipVersionTable v LEFT OUTER JOIN OrleansMembershipTable m 242 | ON v.DeploymentId = m.DeploymentId 243 | WHERE 244 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 245 | '); 246 | 247 | INSERT INTO OrleansQuery(QueryKey, QueryText) 248 | VALUES 249 | ( 250 | 'DeleteMembershipTableEntriesKey',' 251 | DELETE FROM OrleansMembershipTable 252 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 253 | DELETE FROM OrleansMembershipVersionTable 254 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 255 | '); 256 | -------------------------------------------------------------------------------- /StatelessHost/OrleansAdoNetContent/MySQL/MySQL-Main.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation notes: 3 | 4 | 1) The general idea is that data is read and written through Orleans specific queries. 5 | Orleans operates on column names and types when reading and on parameter names and types when writing. 6 | 7 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 8 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 9 | is maintained. 10 | 11 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 12 | by virtue of uniform naming across concrete implementations. 13 | 14 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 15 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 16 | 17 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 18 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 19 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 20 | Orleans handles exception as a failure and will retry. 21 | 22 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 23 | https://dotnet.github.io/orleans/Documentation/Runtime-Implementation-Details/Runtime-Tables.html 24 | https://dotnet.github.io/orleans/Documentation/Runtime-Implementation-Details/Cluster-Management.html 25 | https://github.com/dotnet/orleans/blob/master/src/Orleans.Core/SystemTargetInterfaces/IMembershipTable.cs 26 | */ 27 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 28 | -- these are the only queries Orleans issues to the database. 29 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 30 | CREATE TABLE OrleansQuery 31 | ( 32 | QueryKey VARCHAR(64) NOT NULL, 33 | QueryText VARCHAR(8000) NOT NULL, 34 | 35 | CONSTRAINT OrleansQuery_Key PRIMARY KEY(QueryKey) 36 | ); 37 | -------------------------------------------------------------------------------- /StatelessHost/OrleansAdoNetContent/Oracle/Oracle-Main.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation notes: 3 | 4 | 1) The general idea is that data is read and written through Orleans specific queries. 5 | Orleans operates on column names and types when reading and on parameter names and types when writing. 6 | 7 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 8 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 9 | is maintained. 10 | 11 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 12 | by virtue of uniform naming across concrete implementations. 13 | 14 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 15 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 16 | 17 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 18 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 19 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 20 | Orleans handles exception as a failure and will retry. 21 | 22 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 23 | https://dotnet.github.io/orleans/Documentation/Runtime-Implementation-Details/Runtime-Tables.html 24 | https://dotnet.github.io/orleans/Documentation/Runtime-Implementation-Details/Cluster-Management.html 25 | https://github.com/dotnet/orleans/blob/master/src/Orleans.Core/SystemTargetInterfaces/IMembershipTable.cs 26 | */ 27 | 28 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 29 | -- these are the only queries Orleans issues to the database. 30 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 31 | CREATE TABLE "ORLEANSQUERY" 32 | ( 33 | "QUERYKEY" VARCHAR2(64 BYTE) NOT NULL ENABLE, 34 | "QUERYTEXT" VARCHAR2(4000 BYTE), 35 | 36 | CONSTRAINT "ORLEANSQUERY_PK" PRIMARY KEY ("QUERYKEY") 37 | ); 38 | / 39 | 40 | COMMIT; 41 | 42 | -- Oracle specific implementation note: 43 | -- Some OrleansQueries are implemented as functions and differ from the scripts of other databases. 44 | -- The main reason for this is the fact, that oracle doesn't support returning variables from queries 45 | -- directly. So in the case that a variable value is needed as output of a OrleansQuery (e.g. version) 46 | -- a function is used. 47 | -------------------------------------------------------------------------------- /StatelessHost/OrleansAdoNetContent/PostgreSQL/PostgreSQL-Main.sql: -------------------------------------------------------------------------------- 1 | -- requires Postgres 9.5 (or perhaps higher) 2 | 3 | /* 4 | Implementation notes: 5 | 6 | 1) The general idea is that data is read and written through Orleans specific queries. 7 | Orleans operates on column names and types when reading and on parameter names and types when writing. 8 | 9 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 10 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 11 | is maintained. 12 | 13 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 14 | by virtue of uniform naming across concrete implementations. 15 | 16 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 17 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 18 | 19 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 20 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 21 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 22 | Orleans handles exception as a failure and will retry. 23 | 24 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 25 | https://dotnet.github.io/orleans/Documentation/Runtime-Implementation-Details/Runtime-Tables.html 26 | https://dotnet.github.io/orleans/Documentation/Runtime-Implementation-Details/Cluster-Management.html 27 | https://github.com/dotnet/orleans/blob/master/src/Orleans.Core/SystemTargetInterfaces/IMembershipTable.cs 28 | */ 29 | 30 | 31 | 32 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 33 | -- these are the only queries Orleans issues to the database. 34 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 35 | CREATE TABLE OrleansQuery 36 | ( 37 | QueryKey varchar(64) NOT NULL, 38 | QueryText varchar(8000) NOT NULL, 39 | 40 | CONSTRAINT OrleansQuery_Key PRIMARY KEY(QueryKey) 41 | ); 42 | -------------------------------------------------------------------------------- /StatelessHost/OrleansAdoNetContent/SQLServer/SQLServer-Clustering.sql: -------------------------------------------------------------------------------- 1 | -- For each deployment, there will be only one (active) membership version table version column which will be updated periodically. 2 | CREATE TABLE OrleansMembershipVersionTable 3 | ( 4 | DeploymentId NVARCHAR(150) NOT NULL, 5 | Timestamp DATETIME2(3) NOT NULL DEFAULT GETUTCDATE(), 6 | Version INT NOT NULL DEFAULT 0, 7 | 8 | CONSTRAINT PK_OrleansMembershipVersionTable_DeploymentId PRIMARY KEY(DeploymentId) 9 | ); 10 | 11 | -- Every silo instance has a row in the membership table. 12 | CREATE TABLE OrleansMembershipTable 13 | ( 14 | DeploymentId NVARCHAR(150) NOT NULL, 15 | Address VARCHAR(45) NOT NULL, 16 | Port INT NOT NULL, 17 | Generation INT NOT NULL, 18 | SiloName NVARCHAR(150) NOT NULL, 19 | HostName NVARCHAR(150) NOT NULL, 20 | Status INT NOT NULL, 21 | ProxyPort INT NULL, 22 | SuspectTimes VARCHAR(8000) NULL, 23 | StartTime DATETIME2(3) NOT NULL, 24 | IAmAliveTime DATETIME2(3) NOT NULL, 25 | 26 | CONSTRAINT PK_MembershipTable_DeploymentId PRIMARY KEY(DeploymentId, Address, Port, Generation), 27 | CONSTRAINT FK_MembershipTable_MembershipVersionTable_DeploymentId FOREIGN KEY (DeploymentId) REFERENCES OrleansMembershipVersionTable (DeploymentId) 28 | ); 29 | 30 | INSERT INTO OrleansQuery(QueryKey, QueryText) 31 | VALUES 32 | ( 33 | 'UpdateIAmAlivetimeKey',' 34 | -- This is expected to never fail by Orleans, so return value 35 | -- is not needed nor is it checked. 36 | SET NOCOUNT ON; 37 | UPDATE OrleansMembershipTable 38 | SET 39 | IAmAliveTime = @IAmAliveTime 40 | WHERE 41 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 42 | AND Address = @Address AND @Address IS NOT NULL 43 | AND Port = @Port AND @Port IS NOT NULL 44 | AND Generation = @Generation AND @Generation IS NOT NULL; 45 | '); 46 | 47 | INSERT INTO OrleansQuery(QueryKey, QueryText) 48 | VALUES 49 | ( 50 | 'InsertMembershipVersionKey',' 51 | SET NOCOUNT ON; 52 | INSERT INTO OrleansMembershipVersionTable 53 | ( 54 | DeploymentId 55 | ) 56 | SELECT @DeploymentId 57 | WHERE NOT EXISTS 58 | ( 59 | SELECT 1 60 | FROM 61 | OrleansMembershipVersionTable 62 | WHERE 63 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 64 | ); 65 | 66 | SELECT @@ROWCOUNT; 67 | '); 68 | 69 | INSERT INTO OrleansQuery(QueryKey, QueryText) 70 | VALUES 71 | ( 72 | 'InsertMembershipKey',' 73 | SET XACT_ABORT, NOCOUNT ON; 74 | DECLARE @ROWCOUNT AS INT; 75 | BEGIN TRANSACTION; 76 | INSERT INTO OrleansMembershipTable 77 | ( 78 | DeploymentId, 79 | Address, 80 | Port, 81 | Generation, 82 | SiloName, 83 | HostName, 84 | Status, 85 | ProxyPort, 86 | StartTime, 87 | IAmAliveTime 88 | ) 89 | SELECT 90 | @DeploymentId, 91 | @Address, 92 | @Port, 93 | @Generation, 94 | @SiloName, 95 | @HostName, 96 | @Status, 97 | @ProxyPort, 98 | @StartTime, 99 | @IAmAliveTime 100 | WHERE NOT EXISTS 101 | ( 102 | SELECT 1 103 | FROM 104 | OrleansMembershipTable 105 | WHERE 106 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 107 | AND Address = @Address AND @Address IS NOT NULL 108 | AND Port = @Port AND @Port IS NOT NULL 109 | AND Generation = @Generation AND @Generation IS NOT NULL 110 | ); 111 | 112 | UPDATE OrleansMembershipVersionTable 113 | SET 114 | Timestamp = GETUTCDATE(), 115 | Version = Version + 1 116 | WHERE 117 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 118 | AND Version = @Version AND @Version IS NOT NULL 119 | AND @@ROWCOUNT > 0; 120 | 121 | SET @ROWCOUNT = @@ROWCOUNT; 122 | 123 | IF @ROWCOUNT = 0 124 | ROLLBACK TRANSACTION 125 | ELSE 126 | COMMIT TRANSACTION 127 | SELECT @ROWCOUNT; 128 | '); 129 | 130 | INSERT INTO OrleansQuery(QueryKey, QueryText) 131 | VALUES 132 | ( 133 | 'UpdateMembershipKey',' 134 | SET XACT_ABORT, NOCOUNT ON; 135 | BEGIN TRANSACTION; 136 | 137 | UPDATE OrleansMembershipVersionTable 138 | SET 139 | Timestamp = GETUTCDATE(), 140 | Version = Version + 1 141 | WHERE 142 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 143 | AND Version = @Version AND @Version IS NOT NULL; 144 | 145 | UPDATE OrleansMembershipTable 146 | SET 147 | Status = @Status, 148 | SuspectTimes = @SuspectTimes, 149 | IAmAliveTime = @IAmAliveTime 150 | WHERE 151 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 152 | AND Address = @Address AND @Address IS NOT NULL 153 | AND Port = @Port AND @Port IS NOT NULL 154 | AND Generation = @Generation AND @Generation IS NOT NULL 155 | AND @@ROWCOUNT > 0; 156 | 157 | SELECT @@ROWCOUNT; 158 | COMMIT TRANSACTION; 159 | '); 160 | 161 | INSERT INTO OrleansQuery(QueryKey, QueryText) 162 | VALUES 163 | ( 164 | 'GatewaysQueryKey',' 165 | SELECT 166 | Address, 167 | ProxyPort, 168 | Generation 169 | FROM 170 | OrleansMembershipTable 171 | WHERE 172 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 173 | AND Status = @Status AND @Status IS NOT NULL 174 | AND ProxyPort > 0; 175 | '); 176 | 177 | INSERT INTO OrleansQuery(QueryKey, QueryText) 178 | VALUES 179 | ( 180 | 'MembershipReadRowKey',' 181 | SELECT 182 | v.DeploymentId, 183 | m.Address, 184 | m.Port, 185 | m.Generation, 186 | m.SiloName, 187 | m.HostName, 188 | m.Status, 189 | m.ProxyPort, 190 | m.SuspectTimes, 191 | m.StartTime, 192 | m.IAmAliveTime, 193 | v.Version 194 | FROM 195 | OrleansMembershipVersionTable v 196 | -- This ensures the version table will returned even if there is no matching membership row. 197 | LEFT OUTER JOIN OrleansMembershipTable m ON v.DeploymentId = m.DeploymentId 198 | AND Address = @Address AND @Address IS NOT NULL 199 | AND Port = @Port AND @Port IS NOT NULL 200 | AND Generation = @Generation AND @Generation IS NOT NULL 201 | WHERE 202 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 203 | '); 204 | 205 | INSERT INTO OrleansQuery(QueryKey, QueryText) 206 | VALUES 207 | ( 208 | 'MembershipReadAllKey',' 209 | SELECT 210 | v.DeploymentId, 211 | m.Address, 212 | m.Port, 213 | m.Generation, 214 | m.SiloName, 215 | m.HostName, 216 | m.Status, 217 | m.ProxyPort, 218 | m.SuspectTimes, 219 | m.StartTime, 220 | m.IAmAliveTime, 221 | v.Version 222 | FROM 223 | OrleansMembershipVersionTable v LEFT OUTER JOIN OrleansMembershipTable m 224 | ON v.DeploymentId = m.DeploymentId 225 | WHERE 226 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 227 | '); 228 | 229 | INSERT INTO OrleansQuery(QueryKey, QueryText) 230 | VALUES 231 | ( 232 | 'DeleteMembershipTableEntriesKey',' 233 | DELETE FROM OrleansMembershipTable 234 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 235 | DELETE FROM OrleansMembershipVersionTable 236 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 237 | '); 238 | -------------------------------------------------------------------------------- /StatelessHost/OrleansAdoNetContent/SQLServer/SQLServer-Main.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation notes: 3 | 4 | 1) The general idea is that data is read and written through Orleans specific queries. 5 | Orleans operates on column names and types when reading and on parameter names and types when writing. 6 | 7 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 8 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 9 | is maintained. 10 | 11 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 12 | by virtue of uniform naming across concrete implementations. 13 | 14 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 15 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 16 | 17 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 18 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 19 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 20 | Orleans handles exception as a failure and will retry. 21 | 22 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 23 | https://dotnet.github.io/orleans/Documentation/Runtime-Implementation-Details/Runtime-Tables.html 24 | https://dotnet.github.io/orleans/Documentation/Runtime-Implementation-Details/Cluster-Management.html 25 | https://github.com/dotnet/orleans/blob/master/src/Orleans.Core/SystemTargetInterfaces/IMembershipTable.cs 26 | */ 27 | 28 | -- These settings improves throughput of the database by reducing locking by better separating readers from writers. 29 | -- SQL Server 2012 and newer can refer to itself as CURRENT. Older ones need a workaround. 30 | DECLARE @current NVARCHAR(256); 31 | DECLARE @snapshotSettings NVARCHAR(612); 32 | 33 | SELECT @current = (SELECT DB_NAME()); 34 | SET @snapshotSettings = N'ALTER DATABASE ' + @current + N' SET READ_COMMITTED_SNAPSHOT ON; ALTER DATABASE ' + @current + N' SET ALLOW_SNAPSHOT_ISOLATION ON;'; 35 | 36 | EXECUTE sp_executesql @snapshotSettings; 37 | 38 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 39 | -- these are the only queries Orleans issues to the database. 40 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 41 | CREATE TABLE OrleansQuery 42 | ( 43 | QueryKey VARCHAR(64) NOT NULL, 44 | QueryText VARCHAR(8000) NOT NULL, 45 | 46 | CONSTRAINT OrleansQuery_Key PRIMARY KEY(QueryKey) 47 | ); 48 | -------------------------------------------------------------------------------- /StatelessHost/PackageRoot/Config/Settings.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /StatelessHost/PackageRoot/ServiceManifest.xml: -------------------------------------------------------------------------------- 1 |  2 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | StatelessHost.exe 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /StatelessHost/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.ServiceFabric.Services.Runtime; 2 | using System; 3 | using System.Diagnostics; 4 | using System.Threading; 5 | 6 | namespace StatelessHost 7 | { 8 | internal static class Program 9 | { 10 | /// 11 | /// 这是服务主机进程的入口点。 12 | /// 13 | private static void Main() 14 | { 15 | try 16 | { 17 | // ServiceManifest.XML 文件定义一个或多个服务类型名称。 18 | // 注册服务会将服务类型名称映射到 .NET 类型。 19 | // 在 Service Fabric 创建此服务类型的实例时, 20 | // 会在此主机进程中创建类的实例。 21 | 22 | ServiceRuntime.RegisterServiceAsync("StatelessHostType", 23 | context => new StatelessHost(context)).GetAwaiter().GetResult(); 24 | 25 | ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(StatelessHost).Name); 26 | 27 | // 防止此主机进程终止,以使服务保持运行。 28 | Thread.Sleep(Timeout.Infinite); 29 | } 30 | catch (Exception e) 31 | { 32 | ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString()); 33 | throw; 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /StatelessHost/ServiceEventSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.Tracing; 3 | using System.Fabric; 4 | 5 | namespace StatelessHost 6 | { 7 | [EventSource(Name = "MyCompany-AppCloud-StatelessHost")] 8 | internal sealed class ServiceEventSource : EventSource 9 | { 10 | public static readonly ServiceEventSource Current = new ServiceEventSource(); 11 | 12 | // 实例构造函数专用于强制执行单独语义 13 | private ServiceEventSource() : base() { } 14 | 15 | #region 关键字 16 | // 事件关键字可用于对事件进行分类。 17 | // 每个关键字都是一个位标志。单个事件可与多个关键字关联(通过 EventAttribute.Keywords 属性)。 18 | // 关键字必须定义为 EventSource 内使用它们的、名为“关键字”的公共类。 19 | public static class Keywords 20 | { 21 | public const EventKeywords Requests = (EventKeywords)0x1L; 22 | public const EventKeywords ServiceInitialization = (EventKeywords)0x2L; 23 | } 24 | #endregion 25 | 26 | #region 事件 27 | // 为要对其记录并应用 [Event] 属性的每个事件定义一个实例方法。 28 | // 方法名称是指事件的名称。 29 | // 传递要与事件一起记录的任何参数(仅允许基元整数类型、DateTime、Guid 和字符串)。 30 | // 每个事件方法实现都应检查是否已启用事件源;若已启用,请调用 WriteEvent() 方法来引发事件。 31 | // 传递到每个事件方法的参数数量和类型必须与传递到 WriteEvent() 的完全匹配。 32 | // 在所有不定义事件的方法上放置 [NonEvent] 属性。 33 | // 相关详细信息,请参阅 https://msdn.microsoft.com/zh-cn/library/system.diagnostics.tracing.eventsource.aspx 34 | 35 | [NonEvent] 36 | public void Message(string message, params object[] args) 37 | { 38 | if (this.IsEnabled()) 39 | { 40 | string finalMessage = string.Format(message, args); 41 | Message(finalMessage); 42 | } 43 | } 44 | 45 | private const int MessageEventId = 1; 46 | [Event(MessageEventId, Level = EventLevel.Informational, Message = "{0}")] 47 | public void Message(string message) 48 | { 49 | if (this.IsEnabled()) 50 | { 51 | WriteEvent(MessageEventId, message); 52 | } 53 | } 54 | 55 | [NonEvent] 56 | public void ServiceMessage(StatelessServiceContext serviceContext, string message, params object[] args) 57 | { 58 | if (this.IsEnabled()) 59 | { 60 | string finalMessage = string.Format(message, args); 61 | ServiceMessage( 62 | serviceContext.ServiceName.ToString(), 63 | serviceContext.ServiceTypeName, 64 | serviceContext.InstanceId, 65 | serviceContext.PartitionId, 66 | serviceContext.CodePackageActivationContext.ApplicationName, 67 | serviceContext.CodePackageActivationContext.ApplicationTypeName, 68 | serviceContext.NodeContext.NodeName, 69 | finalMessage); 70 | } 71 | } 72 | 73 | // 对于使用频率很高的事件,用 WriteEventCore API 引发事件可能很有利。 74 | // 这会使参数处理更为高效,但需要显式分配 EventData 结构和不安全代码。 75 | // 若要启用此代码路径,请定义不安全的条件编译符号,并打开项目属性中的不安全代码支持。 76 | private const int ServiceMessageEventId = 2; 77 | [Event(ServiceMessageEventId, Level = EventLevel.Informational, Message = "{7}")] 78 | private 79 | #if UNSAFE 80 | unsafe 81 | #endif 82 | void ServiceMessage( 83 | string serviceName, 84 | string serviceTypeName, 85 | long replicaOrInstanceId, 86 | Guid partitionId, 87 | string applicationName, 88 | string applicationTypeName, 89 | string nodeName, 90 | string message) 91 | { 92 | #if !UNSAFE 93 | WriteEvent(ServiceMessageEventId, serviceName, serviceTypeName, replicaOrInstanceId, partitionId, applicationName, applicationTypeName, nodeName, message); 94 | #else 95 | const int numArgs = 8; 96 | fixed (char* pServiceName = serviceName, pServiceTypeName = serviceTypeName, pApplicationName = applicationName, pApplicationTypeName = applicationTypeName, pNodeName = nodeName, pMessage = message) 97 | { 98 | EventData* eventData = stackalloc EventData[numArgs]; 99 | eventData[0] = new EventData { DataPointer = (IntPtr) pServiceName, Size = SizeInBytes(serviceName) }; 100 | eventData[1] = new EventData { DataPointer = (IntPtr) pServiceTypeName, Size = SizeInBytes(serviceTypeName) }; 101 | eventData[2] = new EventData { DataPointer = (IntPtr) (&replicaOrInstanceId), Size = sizeof(long) }; 102 | eventData[3] = new EventData { DataPointer = (IntPtr) (&partitionId), Size = sizeof(Guid) }; 103 | eventData[4] = new EventData { DataPointer = (IntPtr) pApplicationName, Size = SizeInBytes(applicationName) }; 104 | eventData[5] = new EventData { DataPointer = (IntPtr) pApplicationTypeName, Size = SizeInBytes(applicationTypeName) }; 105 | eventData[6] = new EventData { DataPointer = (IntPtr) pNodeName, Size = SizeInBytes(nodeName) }; 106 | eventData[7] = new EventData { DataPointer = (IntPtr) pMessage, Size = SizeInBytes(message) }; 107 | 108 | WriteEventCore(ServiceMessageEventId, numArgs, eventData); 109 | } 110 | #endif 111 | } 112 | 113 | private const int ServiceTypeRegisteredEventId = 3; 114 | [Event(ServiceTypeRegisteredEventId, Level = EventLevel.Informational, Message = "Service host process {0} registered service type {1}", Keywords = Keywords.ServiceInitialization)] 115 | public void ServiceTypeRegistered(int hostProcessId, string serviceType) 116 | { 117 | WriteEvent(ServiceTypeRegisteredEventId, hostProcessId, serviceType); 118 | } 119 | 120 | private const int ServiceHostInitializationFailedEventId = 4; 121 | [Event(ServiceHostInitializationFailedEventId, Level = EventLevel.Error, Message = "Service host initialization failed", Keywords = Keywords.ServiceInitialization)] 122 | public void ServiceHostInitializationFailed(string exception) 123 | { 124 | WriteEvent(ServiceHostInitializationFailedEventId, exception); 125 | } 126 | 127 | // 与“开始”/“停止”后缀共享相同名称前缀的一对事件会隐式标记事件跟踪活动的边界。 128 | // 这些活动可由调试和分析工具自动捕获,此类工具可计算它们的执行时间、子活动, 129 | // 以及其他统计信息。 130 | private const int ServiceRequestStartEventId = 5; 131 | [Event(ServiceRequestStartEventId, Level = EventLevel.Informational, Message = "Service request '{0}' started", Keywords = Keywords.Requests)] 132 | public void ServiceRequestStart(string requestTypeName) 133 | { 134 | WriteEvent(ServiceRequestStartEventId, requestTypeName); 135 | } 136 | 137 | private const int ServiceRequestStopEventId = 6; 138 | [Event(ServiceRequestStopEventId, Level = EventLevel.Informational, Message = "Service request '{0}' finished", Keywords = Keywords.Requests)] 139 | public void ServiceRequestStop(string requestTypeName, string exception = "") 140 | { 141 | WriteEvent(ServiceRequestStopEventId, requestTypeName, exception); 142 | } 143 | #endregion 144 | 145 | #region 私有方法 146 | #if UNSAFE 147 | private int SizeInBytes(string s) 148 | { 149 | if (s == null) 150 | { 151 | return 0; 152 | } 153 | else 154 | { 155 | return (s.Length + 1) * sizeof(char); 156 | } 157 | } 158 | #endif 159 | #endregion 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /StatelessHost/StartupTask.cs: -------------------------------------------------------------------------------- 1 | using Demo.IGrain; 2 | using Microsoft.Extensions.Logging; 3 | using Orleans; 4 | using Orleans.Runtime; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace StatelessHost 9 | { 10 | public class StartupTask : IStartupTask 11 | { 12 | private readonly IGrainFactory grainFactory; 13 | private readonly ILogger logger; 14 | public StartupTask(IGrainFactory grainFactory, ILogger logger) 15 | { 16 | this.grainFactory = grainFactory; 17 | this.logger = logger; 18 | } 19 | public Task Execute(CancellationToken cancellationToken) 20 | { 21 | //var connectStr = GlobalConfig.Get("all", "Orleans:ConnectionString");//读取全局配置文件示例 22 | var actor = this.grainFactory.GetGrain(0); 23 | Task.Factory.StartNew( 24 | async () => 25 | { 26 | await actor.SetValue("Test Startup"); 27 | } 28 | ); 29 | return Task.CompletedTask; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /StatelessHost/StatelessHost.cs: -------------------------------------------------------------------------------- 1 | using Demo.Grain; 2 | using Lib; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Logging; 6 | using Microsoft.ServiceFabric.Services.Communication.Runtime; 7 | using Microsoft.ServiceFabric.Services.Runtime; 8 | using Orleans; 9 | using Orleans.Configuration; 10 | using Orleans.Hosting; 11 | using Orleans.Hosting.ServiceFabric; 12 | using System; 13 | using System.Collections.Generic; 14 | using System.Fabric; 15 | using System.IO; 16 | using System.Threading; 17 | using System.Threading.Tasks; 18 | 19 | namespace StatelessHost 20 | { 21 | /// 22 | /// 通过 Service Fabric 运行时为每个服务实例创建此类的一个实例。 23 | /// 24 | internal sealed class StatelessHost : StatelessService 25 | { 26 | public StatelessHost(StatelessServiceContext context) 27 | : base(context) 28 | { } 29 | 30 | /// 31 | /// 可选择性地替代以创建侦听器(如 TCP、HTTP),从而使此服务副本可以处理客户端或用户请求。 32 | /// 33 | /// 侦听器集合。 34 | protected override IEnumerable CreateServiceInstanceListeners() 35 | { 36 | var configBuilder = new ConfigurationBuilder() 37 | .SetBasePath(Directory.GetCurrentDirectory()) 38 | .AddJsonFile("appsettings.json", optional: true); 39 | var config = configBuilder.Build(); 40 | GlobalConfig.AddConfigurationObject(config, "all"); 41 | 42 | // Listeners can be opened and closed multiple times over the lifetime of a service instance. 43 | // A new Orleans silo will be both created and initialized each time the listener is opened and will be shutdown 44 | // when the listener is closed. 45 | var listener = OrleansServiceListener.CreateStateless( 46 | (fabricServiceContext, builder) => 47 | { 48 | builder.Configure(options => 49 | { 50 | // The service id is unique for the entire service over its lifetime. This is used to identify persistent state 51 | // such as reminders and grain state. 52 | //options.ServiceId = fabricServiceContext.ServiceName.ToString(); 53 | options.ServiceId = config.GetSection("Orleans:ServiceId").Value; 54 | 55 | // The cluster id identifies a deployed cluster. Since Service Fabric uses rolling upgrades, the cluster id 56 | // can be kept constant. This is used to identify which silos belong to a particular cluster. 57 | options.ClusterId = config.GetSection("Orleans:ClusterId").Value; 58 | }); 59 | 60 | // Configure clustering. Other clustering providers are available, but for the purpose of this sample we 61 | // will use Azure Storage. 62 | // TODO: Pick a clustering provider and configure it here. 63 | //builder.UseAzureStorageClustering(options => options.ConnectionString = "UseDevelopmentStorage=true"); 64 | builder.UseAdoNetClustering(option => 65 | { 66 | option.ConnectionString = config.GetSection("Orleans:ConnectionString").Value; 67 | option.Invariant = config.GetSection("Orleans:Invariant").Value; ; 68 | }); 69 | // Optional: configure logging. 70 | builder.ConfigureLogging(logging => logging.AddConsole()); 71 | builder.AddStartupTask(); 72 | //builder.AddStartupTask(); 73 | 74 | // Service Fabric manages port allocations, so update the configuration using those ports. 75 | // Gather configuration from Service Fabric. 76 | var activation = fabricServiceContext.CodePackageActivationContext; 77 | var endpoints = activation.GetEndpoints(); 78 | 79 | //// These endpoint names correspond to TCP endpoints specified in ServiceManifest.xml 80 | var siloEndpoint = endpoints["OrleansSiloEndpoint"]; 81 | var gatewayEndpoint = endpoints["OrleansProxyEndpoint"]; 82 | var hostname = fabricServiceContext.NodeContext.IPAddressOrFQDN; 83 | builder.ConfigureEndpoints(hostname, siloEndpoint.Port, gatewayEndpoint.Port); 84 | 85 | // Add your application assemblies. 86 | builder.ConfigureApplicationParts(parts => 87 | { 88 | parts.AddApplicationPart(typeof(HelloGrain).Assembly).WithReferences(); 89 | 90 | // Alternative: add all loadable assemblies in the current base path (see AppDomain.BaseDirectory). 91 | parts.AddFromApplicationBaseDirectory(); 92 | }); 93 | builder.UseDashboard(options => 94 | { 95 | options.Username = "Kiwi"; 96 | options.Password = "Kiwi"; 97 | options.Host = "*"; 98 | options.Port = 8080; 99 | options.HostSelf = true; 100 | options.CounterUpdateIntervalMs = 1000; 101 | }); 102 | }); 103 | 104 | return new[] { listener }; 105 | } 106 | 107 | /// 108 | /// 这是服务实例的主入口点。 109 | /// 110 | /// 已在 Service Fabric 需要关闭此服务实例时取消。 111 | protected override async Task RunAsync(CancellationToken cancellationToken) 112 | { 113 | // TODO: 将以下示例代码替换为你自己的逻辑 114 | // 或者在服务不需要此 RunAsync 重写时删除它。 115 | 116 | long iterations = 0; 117 | 118 | while (true) 119 | { 120 | cancellationToken.ThrowIfCancellationRequested(); 121 | 122 | ServiceEventSource.Current.ServiceMessage(this.Context, "Working-{0}", ++iterations); 123 | 124 | await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); 125 | } 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /StatelessHost/StatelessHost.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp2.1 6 | True 7 | win7-x64 8 | False 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Always 18 | PreserveNewest 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /StatelessHost/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Orleans": { 3 | "ConnectionString": "Server=127.0.0.1;Port=5432;Database=Orleans;User Id=postgres;Password=123456;Pooling=false;", 4 | "Invariant": "Npgsql", 5 | "ServiceId": "CoinWeb", 6 | "ClusterId": "CoinV2" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /StatelessWebGo/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using Demo.IGrain; 2 | using Lib; 3 | using Microsoft.AspNetCore.Mvc; 4 | using System.Threading.Tasks; 5 | 6 | namespace StatelessWebGo.Controllers 7 | { 8 | public class HomeController : Controller 9 | { 10 | private IClientFactory clientFactory; 11 | public HomeController(IClientFactory clientFactory) 12 | { 13 | this.clientFactory = clientFactory; 14 | } 15 | public async Task Index() 16 | { 17 | var client = clientFactory.GetClient(); 18 | var actor = client.GetGrain(0); 19 | var r = await actor.SayHello("Kiwi"); 20 | return Content(r); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /StatelessWebGo/OrleansAdoNetContent/MySQL/MySQL-Clustering.sql: -------------------------------------------------------------------------------- 1 | -- For each deployment, there will be only one (active) membership version table version column which will be updated periodically. 2 | CREATE TABLE OrleansMembershipVersionTable 3 | ( 4 | DeploymentId NVARCHAR(150) NOT NULL, 5 | Timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 6 | Version INT NOT NULL DEFAULT 0, 7 | 8 | CONSTRAINT PK_OrleansMembershipVersionTable_DeploymentId PRIMARY KEY(DeploymentId) 9 | ); 10 | 11 | -- Every silo instance has a row in the membership table. 12 | CREATE TABLE OrleansMembershipTable 13 | ( 14 | DeploymentId NVARCHAR(150) NOT NULL, 15 | Address VARCHAR(45) NOT NULL, 16 | Port INT NOT NULL, 17 | Generation INT NOT NULL, 18 | SiloName NVARCHAR(150) NOT NULL, 19 | HostName NVARCHAR(150) NOT NULL, 20 | Status INT NOT NULL, 21 | ProxyPort INT NULL, 22 | SuspectTimes VARCHAR(8000) NULL, 23 | StartTime DATETIME NOT NULL, 24 | IAmAliveTime DATETIME NOT NULL, 25 | 26 | CONSTRAINT PK_MembershipTable_DeploymentId PRIMARY KEY(DeploymentId, Address, Port, Generation), 27 | CONSTRAINT FK_MembershipTable_MembershipVersionTable_DeploymentId FOREIGN KEY (DeploymentId) REFERENCES OrleansMembershipVersionTable (DeploymentId) 28 | ); 29 | 30 | INSERT INTO OrleansQuery(QueryKey, QueryText) 31 | VALUES 32 | ( 33 | 'UpdateIAmAlivetimeKey',' 34 | -- This is expected to never fail by Orleans, so return value 35 | -- is not needed nor is it checked. 36 | UPDATE OrleansMembershipTable 37 | SET 38 | IAmAliveTime = @IAmAliveTime 39 | WHERE 40 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 41 | AND Address = @Address AND @Address IS NOT NULL 42 | AND Port = @Port AND @Port IS NOT NULL 43 | AND Generation = @Generation AND @Generation IS NOT NULL; 44 | '); 45 | 46 | INSERT INTO OrleansQuery(QueryKey, QueryText) 47 | VALUES 48 | ( 49 | 'InsertMembershipVersionKey',' 50 | INSERT INTO OrleansMembershipVersionTable 51 | ( 52 | DeploymentId 53 | ) 54 | SELECT * FROM ( SELECT @DeploymentId ) AS TMP 55 | WHERE NOT EXISTS 56 | ( 57 | SELECT 1 58 | FROM 59 | OrleansMembershipVersionTable 60 | WHERE 61 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 62 | ); 63 | 64 | SELECT ROW_COUNT(); 65 | '); 66 | 67 | INSERT INTO OrleansQuery(QueryKey, QueryText) 68 | VALUES 69 | ( 70 | 'InsertMembershipKey',' 71 | call InsertMembershipKey(@DeploymentId, @Address, @Port, @Generation, 72 | @Version, @SiloName, @HostName, @Status, @ProxyPort, @StartTime, @IAmAliveTime);' 73 | ); 74 | 75 | DELIMITER $$ 76 | 77 | CREATE PROCEDURE InsertMembershipKey( 78 | in _DeploymentId NVARCHAR(150), 79 | in _Address VARCHAR(45), 80 | in _Port INT, 81 | in _Generation INT, 82 | in _Version INT, 83 | in _SiloName NVARCHAR(150), 84 | in _HostName NVARCHAR(150), 85 | in _Status INT, 86 | in _ProxyPort INT, 87 | in _StartTime DATETIME, 88 | in _IAmAliveTime DATETIME 89 | ) 90 | BEGIN 91 | DECLARE _ROWCOUNT INT; 92 | START TRANSACTION; 93 | INSERT INTO OrleansMembershipTable 94 | ( 95 | DeploymentId, 96 | Address, 97 | Port, 98 | Generation, 99 | SiloName, 100 | HostName, 101 | Status, 102 | ProxyPort, 103 | StartTime, 104 | IAmAliveTime 105 | ) 106 | SELECT * FROM ( SELECT 107 | _DeploymentId, 108 | _Address, 109 | _Port, 110 | _Generation, 111 | _SiloName, 112 | _HostName, 113 | _Status, 114 | _ProxyPort, 115 | _StartTime, 116 | _IAmAliveTime) AS TMP 117 | WHERE NOT EXISTS 118 | ( 119 | SELECT 1 120 | FROM 121 | OrleansMembershipTable 122 | WHERE 123 | DeploymentId = _DeploymentId AND _DeploymentId IS NOT NULL 124 | AND Address = _Address AND _Address IS NOT NULL 125 | AND Port = _Port AND _Port IS NOT NULL 126 | AND Generation = _Generation AND _Generation IS NOT NULL 127 | ); 128 | 129 | UPDATE OrleansMembershipVersionTable 130 | SET 131 | Version = Version + 1 132 | WHERE 133 | DeploymentId = _DeploymentId AND _DeploymentId IS NOT NULL 134 | AND Version = _Version AND _Version IS NOT NULL 135 | AND ROW_COUNT() > 0; 136 | 137 | SET _ROWCOUNT = ROW_COUNT(); 138 | 139 | IF _ROWCOUNT = 0 140 | THEN 141 | ROLLBACK; 142 | ELSE 143 | COMMIT; 144 | END IF; 145 | SELECT _ROWCOUNT; 146 | END$$ 147 | 148 | DELIMITER ; 149 | 150 | INSERT INTO OrleansQuery(QueryKey, QueryText) 151 | VALUES 152 | ( 153 | 'UpdateMembershipKey',' 154 | START TRANSACTION; 155 | 156 | UPDATE OrleansMembershipVersionTable 157 | SET 158 | Version = Version + 1 159 | WHERE 160 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 161 | AND Version = @Version AND @Version IS NOT NULL; 162 | 163 | UPDATE OrleansMembershipTable 164 | SET 165 | Status = @Status, 166 | SuspectTimes = @SuspectTimes, 167 | IAmAliveTime = @IAmAliveTime 168 | WHERE 169 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 170 | AND Address = @Address AND @Address IS NOT NULL 171 | AND Port = @Port AND @Port IS NOT NULL 172 | AND Generation = @Generation AND @Generation IS NOT NULL 173 | AND ROW_COUNT() > 0; 174 | 175 | SELECT ROW_COUNT(); 176 | COMMIT; 177 | '); 178 | 179 | INSERT INTO OrleansQuery(QueryKey, QueryText) 180 | VALUES 181 | ( 182 | 'GatewaysQueryKey',' 183 | SELECT 184 | Address, 185 | ProxyPort, 186 | Generation 187 | FROM 188 | OrleansMembershipTable 189 | WHERE 190 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 191 | AND Status = @Status AND @Status IS NOT NULL 192 | AND ProxyPort > 0; 193 | '); 194 | 195 | INSERT INTO OrleansQuery(QueryKey, QueryText) 196 | VALUES 197 | ( 198 | 'MembershipReadRowKey',' 199 | SELECT 200 | v.DeploymentId, 201 | m.Address, 202 | m.Port, 203 | m.Generation, 204 | m.SiloName, 205 | m.HostName, 206 | m.Status, 207 | m.ProxyPort, 208 | m.SuspectTimes, 209 | m.StartTime, 210 | m.IAmAliveTime, 211 | v.Version 212 | FROM 213 | OrleansMembershipVersionTable v 214 | -- This ensures the version table will returned even if there is no matching membership row. 215 | LEFT OUTER JOIN OrleansMembershipTable m ON v.DeploymentId = m.DeploymentId 216 | AND Address = @Address AND @Address IS NOT NULL 217 | AND Port = @Port AND @Port IS NOT NULL 218 | AND Generation = @Generation AND @Generation IS NOT NULL 219 | WHERE 220 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 221 | '); 222 | 223 | INSERT INTO OrleansQuery(QueryKey, QueryText) 224 | VALUES 225 | ( 226 | 'MembershipReadAllKey',' 227 | SELECT 228 | v.DeploymentId, 229 | m.Address, 230 | m.Port, 231 | m.Generation, 232 | m.SiloName, 233 | m.HostName, 234 | m.Status, 235 | m.ProxyPort, 236 | m.SuspectTimes, 237 | m.StartTime, 238 | m.IAmAliveTime, 239 | v.Version 240 | FROM 241 | OrleansMembershipVersionTable v LEFT OUTER JOIN OrleansMembershipTable m 242 | ON v.DeploymentId = m.DeploymentId 243 | WHERE 244 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 245 | '); 246 | 247 | INSERT INTO OrleansQuery(QueryKey, QueryText) 248 | VALUES 249 | ( 250 | 'DeleteMembershipTableEntriesKey',' 251 | DELETE FROM OrleansMembershipTable 252 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 253 | DELETE FROM OrleansMembershipVersionTable 254 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 255 | '); 256 | -------------------------------------------------------------------------------- /StatelessWebGo/OrleansAdoNetContent/MySQL/MySQL-Main.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation notes: 3 | 4 | 1) The general idea is that data is read and written through Orleans specific queries. 5 | Orleans operates on column names and types when reading and on parameter names and types when writing. 6 | 7 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 8 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 9 | is maintained. 10 | 11 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 12 | by virtue of uniform naming across concrete implementations. 13 | 14 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 15 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 16 | 17 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 18 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 19 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 20 | Orleans handles exception as a failure and will retry. 21 | 22 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 23 | https://dotnet.github.io/orleans/Documentation/Runtime-Implementation-Details/Runtime-Tables.html 24 | https://dotnet.github.io/orleans/Documentation/Runtime-Implementation-Details/Cluster-Management.html 25 | https://github.com/dotnet/orleans/blob/master/src/Orleans.Core/SystemTargetInterfaces/IMembershipTable.cs 26 | */ 27 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 28 | -- these are the only queries Orleans issues to the database. 29 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 30 | CREATE TABLE OrleansQuery 31 | ( 32 | QueryKey VARCHAR(64) NOT NULL, 33 | QueryText VARCHAR(8000) NOT NULL, 34 | 35 | CONSTRAINT OrleansQuery_Key PRIMARY KEY(QueryKey) 36 | ); 37 | -------------------------------------------------------------------------------- /StatelessWebGo/OrleansAdoNetContent/Oracle/Oracle-Main.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation notes: 3 | 4 | 1) The general idea is that data is read and written through Orleans specific queries. 5 | Orleans operates on column names and types when reading and on parameter names and types when writing. 6 | 7 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 8 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 9 | is maintained. 10 | 11 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 12 | by virtue of uniform naming across concrete implementations. 13 | 14 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 15 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 16 | 17 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 18 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 19 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 20 | Orleans handles exception as a failure and will retry. 21 | 22 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 23 | https://dotnet.github.io/orleans/Documentation/Runtime-Implementation-Details/Runtime-Tables.html 24 | https://dotnet.github.io/orleans/Documentation/Runtime-Implementation-Details/Cluster-Management.html 25 | https://github.com/dotnet/orleans/blob/master/src/Orleans.Core/SystemTargetInterfaces/IMembershipTable.cs 26 | */ 27 | 28 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 29 | -- these are the only queries Orleans issues to the database. 30 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 31 | CREATE TABLE "ORLEANSQUERY" 32 | ( 33 | "QUERYKEY" VARCHAR2(64 BYTE) NOT NULL ENABLE, 34 | "QUERYTEXT" VARCHAR2(4000 BYTE), 35 | 36 | CONSTRAINT "ORLEANSQUERY_PK" PRIMARY KEY ("QUERYKEY") 37 | ); 38 | / 39 | 40 | COMMIT; 41 | 42 | -- Oracle specific implementation note: 43 | -- Some OrleansQueries are implemented as functions and differ from the scripts of other databases. 44 | -- The main reason for this is the fact, that oracle doesn't support returning variables from queries 45 | -- directly. So in the case that a variable value is needed as output of a OrleansQuery (e.g. version) 46 | -- a function is used. 47 | -------------------------------------------------------------------------------- /StatelessWebGo/OrleansAdoNetContent/PostgreSQL/PostgreSQL-Main.sql: -------------------------------------------------------------------------------- 1 | -- requires Postgres 9.5 (or perhaps higher) 2 | 3 | /* 4 | Implementation notes: 5 | 6 | 1) The general idea is that data is read and written through Orleans specific queries. 7 | Orleans operates on column names and types when reading and on parameter names and types when writing. 8 | 9 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 10 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 11 | is maintained. 12 | 13 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 14 | by virtue of uniform naming across concrete implementations. 15 | 16 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 17 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 18 | 19 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 20 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 21 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 22 | Orleans handles exception as a failure and will retry. 23 | 24 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 25 | https://dotnet.github.io/orleans/Documentation/Runtime-Implementation-Details/Runtime-Tables.html 26 | https://dotnet.github.io/orleans/Documentation/Runtime-Implementation-Details/Cluster-Management.html 27 | https://github.com/dotnet/orleans/blob/master/src/Orleans.Core/SystemTargetInterfaces/IMembershipTable.cs 28 | */ 29 | 30 | 31 | 32 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 33 | -- these are the only queries Orleans issues to the database. 34 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 35 | CREATE TABLE OrleansQuery 36 | ( 37 | QueryKey varchar(64) NOT NULL, 38 | QueryText varchar(8000) NOT NULL, 39 | 40 | CONSTRAINT OrleansQuery_Key PRIMARY KEY(QueryKey) 41 | ); 42 | -------------------------------------------------------------------------------- /StatelessWebGo/OrleansAdoNetContent/SQLServer/SQLServer-Clustering.sql: -------------------------------------------------------------------------------- 1 | -- For each deployment, there will be only one (active) membership version table version column which will be updated periodically. 2 | CREATE TABLE OrleansMembershipVersionTable 3 | ( 4 | DeploymentId NVARCHAR(150) NOT NULL, 5 | Timestamp DATETIME2(3) NOT NULL DEFAULT GETUTCDATE(), 6 | Version INT NOT NULL DEFAULT 0, 7 | 8 | CONSTRAINT PK_OrleansMembershipVersionTable_DeploymentId PRIMARY KEY(DeploymentId) 9 | ); 10 | 11 | -- Every silo instance has a row in the membership table. 12 | CREATE TABLE OrleansMembershipTable 13 | ( 14 | DeploymentId NVARCHAR(150) NOT NULL, 15 | Address VARCHAR(45) NOT NULL, 16 | Port INT NOT NULL, 17 | Generation INT NOT NULL, 18 | SiloName NVARCHAR(150) NOT NULL, 19 | HostName NVARCHAR(150) NOT NULL, 20 | Status INT NOT NULL, 21 | ProxyPort INT NULL, 22 | SuspectTimes VARCHAR(8000) NULL, 23 | StartTime DATETIME2(3) NOT NULL, 24 | IAmAliveTime DATETIME2(3) NOT NULL, 25 | 26 | CONSTRAINT PK_MembershipTable_DeploymentId PRIMARY KEY(DeploymentId, Address, Port, Generation), 27 | CONSTRAINT FK_MembershipTable_MembershipVersionTable_DeploymentId FOREIGN KEY (DeploymentId) REFERENCES OrleansMembershipVersionTable (DeploymentId) 28 | ); 29 | 30 | INSERT INTO OrleansQuery(QueryKey, QueryText) 31 | VALUES 32 | ( 33 | 'UpdateIAmAlivetimeKey',' 34 | -- This is expected to never fail by Orleans, so return value 35 | -- is not needed nor is it checked. 36 | SET NOCOUNT ON; 37 | UPDATE OrleansMembershipTable 38 | SET 39 | IAmAliveTime = @IAmAliveTime 40 | WHERE 41 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 42 | AND Address = @Address AND @Address IS NOT NULL 43 | AND Port = @Port AND @Port IS NOT NULL 44 | AND Generation = @Generation AND @Generation IS NOT NULL; 45 | '); 46 | 47 | INSERT INTO OrleansQuery(QueryKey, QueryText) 48 | VALUES 49 | ( 50 | 'InsertMembershipVersionKey',' 51 | SET NOCOUNT ON; 52 | INSERT INTO OrleansMembershipVersionTable 53 | ( 54 | DeploymentId 55 | ) 56 | SELECT @DeploymentId 57 | WHERE NOT EXISTS 58 | ( 59 | SELECT 1 60 | FROM 61 | OrleansMembershipVersionTable 62 | WHERE 63 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 64 | ); 65 | 66 | SELECT @@ROWCOUNT; 67 | '); 68 | 69 | INSERT INTO OrleansQuery(QueryKey, QueryText) 70 | VALUES 71 | ( 72 | 'InsertMembershipKey',' 73 | SET XACT_ABORT, NOCOUNT ON; 74 | DECLARE @ROWCOUNT AS INT; 75 | BEGIN TRANSACTION; 76 | INSERT INTO OrleansMembershipTable 77 | ( 78 | DeploymentId, 79 | Address, 80 | Port, 81 | Generation, 82 | SiloName, 83 | HostName, 84 | Status, 85 | ProxyPort, 86 | StartTime, 87 | IAmAliveTime 88 | ) 89 | SELECT 90 | @DeploymentId, 91 | @Address, 92 | @Port, 93 | @Generation, 94 | @SiloName, 95 | @HostName, 96 | @Status, 97 | @ProxyPort, 98 | @StartTime, 99 | @IAmAliveTime 100 | WHERE NOT EXISTS 101 | ( 102 | SELECT 1 103 | FROM 104 | OrleansMembershipTable 105 | WHERE 106 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 107 | AND Address = @Address AND @Address IS NOT NULL 108 | AND Port = @Port AND @Port IS NOT NULL 109 | AND Generation = @Generation AND @Generation IS NOT NULL 110 | ); 111 | 112 | UPDATE OrleansMembershipVersionTable 113 | SET 114 | Timestamp = GETUTCDATE(), 115 | Version = Version + 1 116 | WHERE 117 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 118 | AND Version = @Version AND @Version IS NOT NULL 119 | AND @@ROWCOUNT > 0; 120 | 121 | SET @ROWCOUNT = @@ROWCOUNT; 122 | 123 | IF @ROWCOUNT = 0 124 | ROLLBACK TRANSACTION 125 | ELSE 126 | COMMIT TRANSACTION 127 | SELECT @ROWCOUNT; 128 | '); 129 | 130 | INSERT INTO OrleansQuery(QueryKey, QueryText) 131 | VALUES 132 | ( 133 | 'UpdateMembershipKey',' 134 | SET XACT_ABORT, NOCOUNT ON; 135 | BEGIN TRANSACTION; 136 | 137 | UPDATE OrleansMembershipVersionTable 138 | SET 139 | Timestamp = GETUTCDATE(), 140 | Version = Version + 1 141 | WHERE 142 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 143 | AND Version = @Version AND @Version IS NOT NULL; 144 | 145 | UPDATE OrleansMembershipTable 146 | SET 147 | Status = @Status, 148 | SuspectTimes = @SuspectTimes, 149 | IAmAliveTime = @IAmAliveTime 150 | WHERE 151 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 152 | AND Address = @Address AND @Address IS NOT NULL 153 | AND Port = @Port AND @Port IS NOT NULL 154 | AND Generation = @Generation AND @Generation IS NOT NULL 155 | AND @@ROWCOUNT > 0; 156 | 157 | SELECT @@ROWCOUNT; 158 | COMMIT TRANSACTION; 159 | '); 160 | 161 | INSERT INTO OrleansQuery(QueryKey, QueryText) 162 | VALUES 163 | ( 164 | 'GatewaysQueryKey',' 165 | SELECT 166 | Address, 167 | ProxyPort, 168 | Generation 169 | FROM 170 | OrleansMembershipTable 171 | WHERE 172 | DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL 173 | AND Status = @Status AND @Status IS NOT NULL 174 | AND ProxyPort > 0; 175 | '); 176 | 177 | INSERT INTO OrleansQuery(QueryKey, QueryText) 178 | VALUES 179 | ( 180 | 'MembershipReadRowKey',' 181 | SELECT 182 | v.DeploymentId, 183 | m.Address, 184 | m.Port, 185 | m.Generation, 186 | m.SiloName, 187 | m.HostName, 188 | m.Status, 189 | m.ProxyPort, 190 | m.SuspectTimes, 191 | m.StartTime, 192 | m.IAmAliveTime, 193 | v.Version 194 | FROM 195 | OrleansMembershipVersionTable v 196 | -- This ensures the version table will returned even if there is no matching membership row. 197 | LEFT OUTER JOIN OrleansMembershipTable m ON v.DeploymentId = m.DeploymentId 198 | AND Address = @Address AND @Address IS NOT NULL 199 | AND Port = @Port AND @Port IS NOT NULL 200 | AND Generation = @Generation AND @Generation IS NOT NULL 201 | WHERE 202 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 203 | '); 204 | 205 | INSERT INTO OrleansQuery(QueryKey, QueryText) 206 | VALUES 207 | ( 208 | 'MembershipReadAllKey',' 209 | SELECT 210 | v.DeploymentId, 211 | m.Address, 212 | m.Port, 213 | m.Generation, 214 | m.SiloName, 215 | m.HostName, 216 | m.Status, 217 | m.ProxyPort, 218 | m.SuspectTimes, 219 | m.StartTime, 220 | m.IAmAliveTime, 221 | v.Version 222 | FROM 223 | OrleansMembershipVersionTable v LEFT OUTER JOIN OrleansMembershipTable m 224 | ON v.DeploymentId = m.DeploymentId 225 | WHERE 226 | v.DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 227 | '); 228 | 229 | INSERT INTO OrleansQuery(QueryKey, QueryText) 230 | VALUES 231 | ( 232 | 'DeleteMembershipTableEntriesKey',' 233 | DELETE FROM OrleansMembershipTable 234 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 235 | DELETE FROM OrleansMembershipVersionTable 236 | WHERE DeploymentId = @DeploymentId AND @DeploymentId IS NOT NULL; 237 | '); 238 | -------------------------------------------------------------------------------- /StatelessWebGo/OrleansAdoNetContent/SQLServer/SQLServer-Main.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation notes: 3 | 4 | 1) The general idea is that data is read and written through Orleans specific queries. 5 | Orleans operates on column names and types when reading and on parameter names and types when writing. 6 | 7 | 2) The implementations *must* preserve input and output names and types. Orleans uses these parameters to reads query results by name and type. 8 | Vendor and deployment specific tuning is allowed and contributions are encouraged as long as the interface contract 9 | is maintained. 10 | 11 | 3) The implementation across vendor specific scripts *should* preserve the constraint names. This simplifies troubleshooting 12 | by virtue of uniform naming across concrete implementations. 13 | 14 | 5) ETag for Orleans is an opaque column that represents a unique version. The type of its actual implementation 15 | is not important as long as it represents a unique version. In this implementation we use integers for versioning 16 | 17 | 6) For the sake of being explicit and removing ambiguity, Orleans expects some queries to return either TRUE as >0 value 18 | or FALSE as =0 value. That is, affected rows or such does not matter. If an error is raised or an exception is thrown 19 | the query *must* ensure the entire transaction is rolled back and may either return FALSE or propagate the exception. 20 | Orleans handles exception as a failure and will retry. 21 | 22 | 7) The implementation follows the Extended Orleans membership protocol. For more information, see at: 23 | https://dotnet.github.io/orleans/Documentation/Runtime-Implementation-Details/Runtime-Tables.html 24 | https://dotnet.github.io/orleans/Documentation/Runtime-Implementation-Details/Cluster-Management.html 25 | https://github.com/dotnet/orleans/blob/master/src/Orleans.Core/SystemTargetInterfaces/IMembershipTable.cs 26 | */ 27 | 28 | -- These settings improves throughput of the database by reducing locking by better separating readers from writers. 29 | -- SQL Server 2012 and newer can refer to itself as CURRENT. Older ones need a workaround. 30 | DECLARE @current NVARCHAR(256); 31 | DECLARE @snapshotSettings NVARCHAR(612); 32 | 33 | SELECT @current = (SELECT DB_NAME()); 34 | SET @snapshotSettings = N'ALTER DATABASE ' + @current + N' SET READ_COMMITTED_SNAPSHOT ON; ALTER DATABASE ' + @current + N' SET ALLOW_SNAPSHOT_ISOLATION ON;'; 35 | 36 | EXECUTE sp_executesql @snapshotSettings; 37 | 38 | -- This table defines Orleans operational queries. Orleans uses these to manage its operations, 39 | -- these are the only queries Orleans issues to the database. 40 | -- These can be redefined (e.g. to provide non-destructive updates) provided the stated interface principles hold. 41 | CREATE TABLE OrleansQuery 42 | ( 43 | QueryKey VARCHAR(64) NOT NULL, 44 | QueryText VARCHAR(8000) NOT NULL, 45 | 46 | CONSTRAINT OrleansQuery_Key PRIMARY KEY(QueryKey) 47 | ); 48 | -------------------------------------------------------------------------------- /StatelessWebGo/PackageRoot/Config/Settings.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /StatelessWebGo/PackageRoot/ServiceManifest.xml: -------------------------------------------------------------------------------- 1 |  2 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | StatelessWebGo.exe 18 | CodePackage 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /StatelessWebGo/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.ServiceFabric.Services.Runtime; 2 | using System; 3 | using System.Diagnostics; 4 | using System.Threading; 5 | 6 | namespace StatelessWebGo 7 | { 8 | internal static class Program 9 | { 10 | /// 11 | /// 这是服务主机进程的入口点。 12 | /// 13 | private static void Main() 14 | { 15 | try 16 | { 17 | // ServiceManifest.XML 文件定义一个或多个服务类型名称。 18 | // 注册服务会将服务类型名称映射到 .NET 类型。 19 | // 在 Service Fabric 创建此服务类型的实例时, 20 | // 会在此主机进程中创建类的实例。 21 | 22 | ServiceRuntime.RegisterServiceAsync("StatelessWebGoType", 23 | context => new StatelessWebGo(context)).GetAwaiter().GetResult(); 24 | 25 | ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(StatelessWebGo).Name); 26 | 27 | // 防止此主机进程终止,以使服务保持运行。 28 | Thread.Sleep(Timeout.Infinite); 29 | } 30 | catch (Exception e) 31 | { 32 | ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString()); 33 | throw; 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /StatelessWebGo/ServiceEventSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.Tracing; 3 | using System.Fabric; 4 | using System.Threading.Tasks; 5 | 6 | namespace StatelessWebGo 7 | { 8 | [EventSource(Name = "MyCompany-AppCloud-StatelessWebGo")] 9 | internal sealed class ServiceEventSource : EventSource 10 | { 11 | public static readonly ServiceEventSource Current = new ServiceEventSource(); 12 | 13 | static ServiceEventSource() 14 | { 15 | // 一种解决方法,用于解决在初始化任务基础结构之前不会跟踪 ETW 活动的问题。 16 | // 此问题将在 .NET Framework 4.6.2 中得到解决。 17 | Task.Run(() => { }); 18 | } 19 | 20 | // 实例构造函数专用于强制执行单独语义 21 | private ServiceEventSource() : base() { } 22 | 23 | #region 关键字 24 | // 事件关键字可用于对事件进行分类。 25 | // 每个关键字都是一个位标志。单个事件可与多个关键字关联(通过 EventAttribute.Keywords 属性)。 26 | // 关键字必须定义为 EventSource 内使用它们的、名为“关键字”的公共类。 27 | public static class Keywords 28 | { 29 | public const EventKeywords Requests = (EventKeywords)0x1L; 30 | public const EventKeywords ServiceInitialization = (EventKeywords)0x2L; 31 | } 32 | #endregion 33 | 34 | #region 事件 35 | // 为要对其记录并应用 [Event] 属性的每个事件定义一个实例方法。 36 | // 方法名称是指事件的名称。 37 | // 传递要与事件一起记录的任何参数(仅允许基元整数类型、DateTime、Guid 和字符串)。 38 | // 每个事件方法实现都应检查是否已启用事件源;若已启用,请调用 WriteEvent() 方法来引发事件。 39 | // 传递到每个事件方法的参数数量和类型必须与传递到 WriteEvent() 的完全匹配。 40 | // 在所有不定义事件的方法上放置 [NonEvent] 属性。 41 | // 相关详细信息,请参阅 https://msdn.microsoft.com/zh-cn/library/system.diagnostics.tracing.eventsource.aspx 42 | 43 | [NonEvent] 44 | public void Message(string message, params object[] args) 45 | { 46 | if (this.IsEnabled()) 47 | { 48 | string finalMessage = string.Format(message, args); 49 | Message(finalMessage); 50 | } 51 | } 52 | 53 | private const int MessageEventId = 1; 54 | [Event(MessageEventId, Level = EventLevel.Informational, Message = "{0}")] 55 | public void Message(string message) 56 | { 57 | if (this.IsEnabled()) 58 | { 59 | WriteEvent(MessageEventId, message); 60 | } 61 | } 62 | 63 | [NonEvent] 64 | public void ServiceMessage(ServiceContext serviceContext, string message, params object[] args) 65 | { 66 | if (this.IsEnabled()) 67 | { 68 | 69 | string finalMessage = string.Format(message, args); 70 | ServiceMessage( 71 | serviceContext.ServiceName.ToString(), 72 | serviceContext.ServiceTypeName, 73 | GetReplicaOrInstanceId(serviceContext), 74 | serviceContext.PartitionId, 75 | serviceContext.CodePackageActivationContext.ApplicationName, 76 | serviceContext.CodePackageActivationContext.ApplicationTypeName, 77 | serviceContext.NodeContext.NodeName, 78 | finalMessage); 79 | } 80 | } 81 | 82 | // 对于使用频率很高的事件,用 WriteEventCore API 引发事件可能很有利。 83 | // 这会使参数处理更为高效,但需要显式分配 EventData 结构和不安全代码。 84 | // 若要启用此代码路径,请定义不安全的条件编译符号,并打开项目属性中的不安全代码支持。 85 | private const int ServiceMessageEventId = 2; 86 | [Event(ServiceMessageEventId, Level = EventLevel.Informational, Message = "{7}")] 87 | private 88 | #if UNSAFE 89 | unsafe 90 | #endif 91 | void ServiceMessage( 92 | string serviceName, 93 | string serviceTypeName, 94 | long replicaOrInstanceId, 95 | Guid partitionId, 96 | string applicationName, 97 | string applicationTypeName, 98 | string nodeName, 99 | string message) 100 | { 101 | #if !UNSAFE 102 | WriteEvent(ServiceMessageEventId, serviceName, serviceTypeName, replicaOrInstanceId, partitionId, applicationName, applicationTypeName, nodeName, message); 103 | #else 104 | const int numArgs = 8; 105 | fixed (char* pServiceName = serviceName, pServiceTypeName = serviceTypeName, pApplicationName = applicationName, pApplicationTypeName = applicationTypeName, pNodeName = nodeName, pMessage = message) 106 | { 107 | EventData* eventData = stackalloc EventData[numArgs]; 108 | eventData[0] = new EventData { DataPointer = (IntPtr) pServiceName, Size = SizeInBytes(serviceName) }; 109 | eventData[1] = new EventData { DataPointer = (IntPtr) pServiceTypeName, Size = SizeInBytes(serviceTypeName) }; 110 | eventData[2] = new EventData { DataPointer = (IntPtr) (&replicaOrInstanceId), Size = sizeof(long) }; 111 | eventData[3] = new EventData { DataPointer = (IntPtr) (&partitionId), Size = sizeof(Guid) }; 112 | eventData[4] = new EventData { DataPointer = (IntPtr) pApplicationName, Size = SizeInBytes(applicationName) }; 113 | eventData[5] = new EventData { DataPointer = (IntPtr) pApplicationTypeName, Size = SizeInBytes(applicationTypeName) }; 114 | eventData[6] = new EventData { DataPointer = (IntPtr) pNodeName, Size = SizeInBytes(nodeName) }; 115 | eventData[7] = new EventData { DataPointer = (IntPtr) pMessage, Size = SizeInBytes(message) }; 116 | 117 | WriteEventCore(ServiceMessageEventId, numArgs, eventData); 118 | } 119 | #endif 120 | } 121 | 122 | private const int ServiceTypeRegisteredEventId = 3; 123 | [Event(ServiceTypeRegisteredEventId, Level = EventLevel.Informational, Message = "Service host process {0} registered service type {1}", Keywords = Keywords.ServiceInitialization)] 124 | public void ServiceTypeRegistered(int hostProcessId, string serviceType) 125 | { 126 | WriteEvent(ServiceTypeRegisteredEventId, hostProcessId, serviceType); 127 | } 128 | 129 | private const int ServiceHostInitializationFailedEventId = 4; 130 | [Event(ServiceHostInitializationFailedEventId, Level = EventLevel.Error, Message = "Service host initialization failed", Keywords = Keywords.ServiceInitialization)] 131 | public void ServiceHostInitializationFailed(string exception) 132 | { 133 | WriteEvent(ServiceHostInitializationFailedEventId, exception); 134 | } 135 | 136 | // 与“开始”/“停止”后缀共享相同名称前缀的一对事件会隐式标记事件跟踪活动的边界。 137 | // 这些活动可由调试和分析工具自动捕获,此类工具可计算它们的执行时间、子活动, 138 | // 以及其他统计信息。 139 | private const int ServiceRequestStartEventId = 5; 140 | [Event(ServiceRequestStartEventId, Level = EventLevel.Informational, Message = "Service request '{0}' started", Keywords = Keywords.Requests)] 141 | public void ServiceRequestStart(string requestTypeName) 142 | { 143 | WriteEvent(ServiceRequestStartEventId, requestTypeName); 144 | } 145 | 146 | private const int ServiceRequestStopEventId = 6; 147 | [Event(ServiceRequestStopEventId, Level = EventLevel.Informational, Message = "Service request '{0}' finished", Keywords = Keywords.Requests)] 148 | public void ServiceRequestStop(string requestTypeName, string exception = "") 149 | { 150 | WriteEvent(ServiceRequestStopEventId, requestTypeName, exception); 151 | } 152 | #endregion 153 | 154 | #region 私有方法 155 | private static long GetReplicaOrInstanceId(ServiceContext context) 156 | { 157 | StatelessServiceContext stateless = context as StatelessServiceContext; 158 | if (stateless != null) 159 | { 160 | return stateless.InstanceId; 161 | } 162 | 163 | StatefulServiceContext stateful = context as StatefulServiceContext; 164 | if (stateful != null) 165 | { 166 | return stateful.ReplicaId; 167 | } 168 | 169 | throw new NotSupportedException("Context type not supported."); 170 | } 171 | #if UNSAFE 172 | private int SizeInBytes(string s) 173 | { 174 | if (s == null) 175 | { 176 | return 0; 177 | } 178 | else 179 | { 180 | return (s.Length + 1) * sizeof(char); 181 | } 182 | } 183 | #endif 184 | #endregion 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /StatelessWebGo/Startup.cs: -------------------------------------------------------------------------------- 1 | using Lib; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using System.IO; 9 | 10 | namespace StatelessWebGo 11 | { 12 | public class Startup 13 | { 14 | public Startup(IConfiguration configuration) 15 | { 16 | var builder = new ConfigurationBuilder() 17 | .AddConfiguration(configuration) 18 | .SetBasePath(Directory.GetCurrentDirectory()) 19 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) 20 | .AddEnvironmentVariables(); 21 | Configuration = builder.Build(); 22 | GlobalConfig.AddConfigurationObject(Configuration, "all"); 23 | } 24 | 25 | public IConfiguration Configuration { get; } 26 | 27 | // This method gets called by the runtime. Use this method to add services to the container. 28 | public void ConfigureServices(IServiceCollection services) 29 | { 30 | services.Configure(options => 31 | { 32 | // This lambda determines whether user consent for non-essential cookies is needed for a given request. 33 | options.CheckConsentNeeded = context => true; 34 | options.MinimumSameSitePolicy = SameSiteMode.None; 35 | }); 36 | 37 | 38 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 39 | services.AddSingleton(); 40 | } 41 | 42 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 43 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 44 | { 45 | if (env.IsDevelopment()) 46 | { 47 | app.UseDeveloperExceptionPage(); 48 | } 49 | else 50 | { 51 | app.UseExceptionHandler("/Home/Error"); 52 | } 53 | 54 | app.UseStaticFiles(); 55 | app.UseCookiePolicy(); 56 | 57 | app.UseMvc(routes => 58 | { 59 | routes.MapRoute( 60 | name: "default", 61 | template: "{controller=Home}/{action=Index}/{id?}"); 62 | }); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /StatelessWebGo/StatelessWebGo.cs: -------------------------------------------------------------------------------- 1 | using Demo.IGrain; 2 | using Lib; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Logging; 6 | using Microsoft.ServiceFabric.Services.Communication.AspNetCore; 7 | using Microsoft.ServiceFabric.Services.Communication.Runtime; 8 | using Microsoft.ServiceFabric.Services.Runtime; 9 | using Orleans; 10 | using Orleans.Runtime; 11 | using System; 12 | using System.Collections.Generic; 13 | using System.Fabric; 14 | using System.IO; 15 | using System.Threading.Tasks; 16 | using Orleans.Configuration; 17 | using Orleans.Hosting; 18 | 19 | namespace StatelessWebGo 20 | { 21 | /// 22 | /// FabricRuntime 为每个服务类型实例创建此类的一个实例。 23 | /// 24 | internal sealed class StatelessWebGo : StatelessService 25 | { 26 | public StatelessWebGo(StatelessServiceContext context) 27 | : base(context) 28 | { } 29 | 30 | /// 31 | /// 可选择性地替代以创建此服务实例的侦听器(如 TCP、http)。 32 | /// 33 | /// 侦听器集合。 34 | protected override IEnumerable CreateServiceInstanceListeners() 35 | { 36 | return new ServiceInstanceListener[] 37 | { 38 | new ServiceInstanceListener(serviceContext => 39 | new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) => 40 | { 41 | StartClientWithRetries().GetAwaiter().GetResult(); 42 | ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}"); 43 | 44 | return new WebHostBuilder() 45 | .UseKestrel() 46 | .ConfigureServices( 47 | services => services 48 | .AddSingleton(serviceContext)) 49 | .UseContentRoot(Directory.GetCurrentDirectory()) 50 | .UseStartup() 51 | .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None) 52 | .UseUrls(url) 53 | .Build(); 54 | })) 55 | }; 56 | } 57 | private static async Task StartClientWithRetries(int initializeAttemptsBeforeFailing = 5) 58 | { 59 | var serviceName = new Uri("fabric:/AppCloud/StatelessHost"); 60 | const string invariant = "Npgsql"; 61 | const string connectionString = "Server=127.0.0.1;Port=5432;Database=Orleans;User Id=postgres;Password=123456;Pooling=false;"; 62 | int attempt = 0; 63 | IClusterClient client; 64 | while (true) 65 | { 66 | try 67 | { 68 | client = await ClientFactory.Build(() => 69 | { 70 | var builder = new ClientBuilder() 71 | //.UseLocalhostClustering() 72 | .Configure(options => 73 | { 74 | //options.ServiceId =serviceName.ToString(); 75 | options.ServiceId = "CoinWeb"; 76 | options.ClusterId = "CoinV2"; 77 | }) 78 | .UseAdoNetClustering(option => 79 | { 80 | option.ConnectionString = connectionString; 81 | option.Invariant = invariant; 82 | }) 83 | .ConfigureApplicationParts(parts => parts.AddApplicationPart(typeof(IHello).Assembly).WithReferences()) 84 | .ConfigureLogging(logging => logging.AddConsole()); 85 | return builder; 86 | }); 87 | Console.WriteLine("Client successfully connect to silo host"); 88 | break; 89 | } 90 | catch (SiloUnavailableException) 91 | { 92 | attempt++; 93 | Console.WriteLine($"Attempt {attempt} of {initializeAttemptsBeforeFailing} failed to initialize the Orleans client."); 94 | if (attempt > initializeAttemptsBeforeFailing) 95 | { 96 | throw; 97 | } 98 | await Task.Delay(TimeSpan.FromSeconds(4)); 99 | } 100 | } 101 | 102 | return client; 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /StatelessWebGo/StatelessWebGo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | True 6 | win7-x64 7 | False 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | C:\Users\Eric\.nuget\packages\microsoft.orleans.clustering.adonet\2.0.0\lib\netstandard2.0\Orleans.Clustering.AdoNet.dll 26 | 27 | 28 | 29 | 30 | 31 | Always 32 | 33 | 34 | Always 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /StatelessWebGo/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /StatelessWebGo/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Orleans": { 3 | "ConnectionString": "Server=127.0.0.1;Port=5432;Database=Orleans;User Id=postgres;Password=123456;Pooling=false;", 4 | "Invariant": "Npgsql", 5 | "ServiceId": "CoinWeb", 6 | "ClusterId": "CoinV2" 7 | } 8 | } 9 | --------------------------------------------------------------------------------