├── .gitattributes ├── .gitignore ├── .nuget ├── NuGet.Config ├── NuGet.exe └── NuGet.targets ├── Amazon.ElastiCacheCluster ├── Amazon.ElastiCacheCluster.csproj ├── Amazon.ElastiCacheCluster.nuspec ├── ClusterClient.cs ├── ClusterConfigSettings.cs ├── ConfigurationPoller.cs ├── DiscoveryNode.cs ├── ElastiCacheClusterConfig.cs ├── Factories │ ├── DefaultConfigNodeFactory.cs │ └── IConfigNodeFactory.cs ├── Helpers │ ├── AddrUtil.cs │ └── TextSocketHelper.cs ├── Operations │ ├── ConfigGetOperation.cs │ ├── GetHelper.cs │ ├── GetOperation.cs │ └── IConfigOperation.cs ├── Pools │ ├── AutoBinaryPool.cs │ └── AutoServerPool.cs ├── Properties │ └── AssemblyInfo.cs ├── awskey.snk └── packages.config ├── ClusterClientAppTester ├── App.config ├── ClusterClientAppTester.csproj ├── Form1.Designer.cs ├── Form1.cs ├── Form1.resx ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings └── packages.config ├── ElastiCacheClusterConfig.sln ├── LICENSE.txt ├── LocalSimulationTests ├── ConfigTests.cs ├── LocalSimulationTests.csproj ├── NodeFactory.cs ├── Properties │ └── AssemblyInfo.cs ├── TestNode.cs └── packages.config ├── NOTICE.txt ├── README.md └── packages └── repositories.config /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | 18 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 19 | !packages/*/build/ 20 | 21 | # MSTest test Results 22 | [Tt]est[Rr]esult*/ 23 | [Bb]uild[Ll]og.* 24 | 25 | *_i.c 26 | *_p.c 27 | *.ilk 28 | *.meta 29 | *.obj 30 | *.pch 31 | *.pdb 32 | *.pgc 33 | *.pgd 34 | *.rsp 35 | *.sbr 36 | *.tlb 37 | *.tli 38 | *.tlh 39 | *.tmp 40 | *.tmp_proj 41 | *.log 42 | *.vspscc 43 | *.vssscc 44 | .builds 45 | *.pidb 46 | *.log 47 | *.scc 48 | 49 | # Visual C++ cache files 50 | ipch/ 51 | *.aps 52 | *.ncb 53 | *.opensdf 54 | *.sdf 55 | *.cachefile 56 | 57 | # Visual Studio profiler 58 | *.psess 59 | *.vsp 60 | *.vspx 61 | 62 | # Guidance Automation Toolkit 63 | *.gpState 64 | 65 | # ReSharper is a .NET coding add-in 66 | _ReSharper*/ 67 | *.[Rr]e[Ss]harper 68 | 69 | # TeamCity is a build add-in 70 | _TeamCity* 71 | 72 | # DotCover is a Code Coverage Tool 73 | *.dotCover 74 | 75 | # NCrunch 76 | *.ncrunch* 77 | .*crunch*.local.xml 78 | 79 | # Installshield output folder 80 | [Ee]xpress/ 81 | 82 | # DocProject is a documentation generator add-in 83 | DocProject/buildhelp/ 84 | DocProject/Help/*.HxT 85 | DocProject/Help/*.HxC 86 | DocProject/Help/*.hhc 87 | DocProject/Help/*.hhk 88 | DocProject/Help/*.hhp 89 | DocProject/Help/Html2 90 | DocProject/Help/html 91 | 92 | # Click-Once directory 93 | publish/ 94 | 95 | # Publish Web Output 96 | *.Publish.xml 97 | 98 | # NuGet Packages Directory 99 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 100 | #packages/ 101 | 102 | # Windows Azure Build Output 103 | csx 104 | *.build.csdef 105 | 106 | # Windows Store app package directory 107 | AppPackages/ 108 | 109 | # Others 110 | sql/ 111 | *.Cache 112 | ClientBin/ 113 | [Ss]tyle[Cc]op.* 114 | ~$* 115 | *~ 116 | *.dbmdl 117 | *.[Pp]ublish.xml 118 | *.pfx 119 | *.publishsettings 120 | 121 | # RIA/Silverlight projects 122 | Generated_Code/ 123 | 124 | # Backup & report files from converting an old project file to a newer 125 | # Visual Studio version. Backup files are not needed, because we have git ;-) 126 | _UpgradeReport_Files/ 127 | Backup*/ 128 | UpgradeLog*.XML 129 | UpgradeLog*.htm 130 | 131 | # SQL Server files 132 | App_Data/*.mdf 133 | App_Data/*.ldf 134 | 135 | 136 | #LightSwitch generated files 137 | GeneratedArtifacts/ 138 | _Pvt_Extensions/ 139 | ModelManifest.xml 140 | 141 | # ========================= 142 | # Windows detritus 143 | # ========================= 144 | 145 | # Windows image file caches 146 | Thumbs.db 147 | ehthumbs.db 148 | 149 | # Folder config file 150 | Desktop.ini 151 | 152 | # Recycle Bin used on file shares 153 | $RECYCLE.BIN/ 154 | 155 | # Mac desktop service store files 156 | .DS_Store 157 | -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/elasticache-cluster-config-net/30996c7f37dbb978f7d25f926847852a1d6ea50f/.nuget/NuGet.exe -------------------------------------------------------------------------------- /.nuget/NuGet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildProjectDirectory)\..\ 5 | 6 | 7 | false 8 | 9 | 10 | false 11 | 12 | 13 | true 14 | 15 | 16 | false 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) 31 | 32 | 33 | 34 | 35 | $(SolutionDir).nuget 36 | 37 | 38 | 39 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config 40 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config 41 | 42 | 43 | 44 | $(MSBuildProjectDirectory)\packages.config 45 | $(PackagesProjectConfig) 46 | 47 | 48 | 49 | 50 | $(NuGetToolsPath)\NuGet.exe 51 | @(PackageSource) 52 | 53 | "$(NuGetExePath)" 54 | mono --runtime=v4.0.30319 "$(NuGetExePath)" 55 | 56 | $(TargetDir.Trim('\\')) 57 | 58 | -RequireConsent 59 | -NonInteractive 60 | 61 | "$(SolutionDir) " 62 | "$(SolutionDir)" 63 | 64 | 65 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) 66 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols 67 | 68 | 69 | 70 | RestorePackages; 71 | $(BuildDependsOn); 72 | 73 | 74 | 75 | 76 | $(BuildDependsOn); 77 | BuildPackage; 78 | 79 | 80 | 81 | 82 | 83 | 84 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 99 | 100 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/Amazon.ElastiCacheCluster.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Release 6 | AnyCPU 7 | {1255EBD3-0340-4AF6-A5F2-3D97D8709546} 8 | Library 9 | Properties 10 | Amazon.ElastiCacheCluster 11 | Amazon.ElastiCacheCluster 12 | v3.5 13 | 512 14 | 15 | ..\..\..\reInvent2014Demos\ZipCodes\ 16 | true 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | 24 | 25 | prompt 26 | 4 27 | true 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | 34 | 35 | prompt 36 | 4 37 | true 38 | bin\Release\Amazon.ElastiCacheCluster.XML 39 | 40 | 41 | true 42 | 43 | 44 | awskey.snk 45 | 46 | 47 | 48 | ..\packages\EnyimMemcached.2.12\lib\net35\Enyim.Caching.dll 49 | True 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 86 | 87 | 88 | 89 | 96 | -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/Amazon.ElastiCacheCluster.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $id$ 5 | $version$ 6 | $title$ 7 | $author$ 8 | $author$ 9 | http://media.amazonwebservices.com/aws_singlebox_01.png 10 | http://aws.amazon.com/apache2.0 11 | https://github.com/awslabs/elasticache-cluster-config-net 12 | false 13 | $description$ 14 | Copyright 2014 15 | AWS Amazon cloud elasticache clusterclient aws amazon ElastiCacheCluster elasticacheclusterconfig cluster config clusterconfig elasticachenetclusterclient ElastiCache 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/ClusterClient.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Text; 19 | using Enyim.Caching; 20 | 21 | namespace Amazon.ElastiCacheCluster 22 | { 23 | /// 24 | /// Used to instantiate MemcachedClients with auto discovery enabled. 25 | /// Only use these for easy creation because the ability to get information from the config object is lost 26 | /// 27 | public static class ClusterClient 28 | { 29 | /// 30 | /// Creates a MemcachedClient using the settings found in the app.config section "clusterclient" 31 | /// 32 | /// A new MemcachedClient configured for auto discovery 33 | public static MemcachedClient CreateClient() 34 | { 35 | return new MemcachedClient(new ElastiCacheClusterConfig()); 36 | } 37 | 38 | /// 39 | /// Creates a MemcachedClient using the settings found in the app.config section specified 40 | /// 41 | /// A section in app.config that has a endpoint field 42 | /// A new MemcachedClient configured for auto discovery 43 | public static MemcachedClient CreateClient(string section) 44 | { 45 | return new MemcachedClient(new ElastiCacheClusterConfig(section)); 46 | } 47 | 48 | /// 49 | /// Creates a MemcachedClient using the default settings with the endpoint and port specified 50 | /// 51 | /// The url for the cluster endpoint containing .cfg. 52 | /// The port to access the cluster on 53 | /// A new MemcachedClient configured for auto discovery 54 | public static MemcachedClient CreateClient(string endpoint, int port) 55 | { 56 | return new MemcachedClient(new ElastiCacheClusterConfig(endpoint, port)); 57 | } 58 | 59 | /// 60 | /// Creates a MemcachedClient using the Client config provided 61 | /// 62 | /// The config to instantiate the client with 63 | /// A new MemcachedClient configured for auto discovery 64 | public static MemcachedClient CreateClient(ElastiCacheClusterConfig config) 65 | { 66 | return new MemcachedClient(config); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/ClusterConfigSettings.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Portions copyright 2010 Attila Kiskó, enyim.com. Please see LICENSE.txt 5 | * for applicable license terms and NOTICE.txt for applicable notices. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"). 8 | * You may not use this file except in compliance with the License. 9 | * A copy of the License is located at 10 | * 11 | * http://aws.amazon.com/apache2.0 12 | * 13 | * or in the "license" file accompanying this file. This file is distributed 14 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 15 | * express or implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | */ 18 | using System; 19 | using System.Collections.Generic; 20 | using System.Linq; 21 | using System.Text; 22 | using System.Configuration; 23 | using Enyim.Caching.Configuration; 24 | using Enyim.Caching.Memcached; 25 | using Amazon.ElastiCacheCluster.Factories; 26 | 27 | namespace Amazon.ElastiCacheCluster 28 | { 29 | /// 30 | /// A config settings object used to configure the client config 31 | /// 32 | public class ClusterConfigSettings : ConfigurationSection 33 | { 34 | /// 35 | /// An object that produces nodes for the Discovery Node, mainly used for testing 36 | /// 37 | public IConfigNodeFactory NodeFactory { get; set; } 38 | 39 | #region Constructors 40 | 41 | /// 42 | /// For config manager 43 | /// 44 | public ClusterConfigSettings() { } 45 | 46 | /// 47 | /// Used to initialize a setup with a host and port 48 | /// 49 | /// Cluster hostname 50 | /// Cluster port 51 | public ClusterConfigSettings(string hostname, int port) 52 | { 53 | if (string.IsNullOrEmpty(hostname)) 54 | throw new ArgumentNullException("hostname"); 55 | if (port <= 0) 56 | throw new ArgumentException("Port cannot be less than or equal to zero"); 57 | 58 | this.ClusterEndPoint.HostName = hostname; 59 | this.ClusterEndPoint.Port = port; 60 | } 61 | 62 | #endregion 63 | 64 | #region Config Settings 65 | 66 | /// 67 | /// Class containing information about the cluster host and port 68 | /// 69 | [ConfigurationProperty("endpoint", IsRequired = true)] 70 | public Endpoint ClusterEndPoint 71 | { 72 | get { return (Endpoint)base["endpoint"]; } 73 | set { base["endpoint"] = value; } 74 | } 75 | 76 | /// 77 | /// Class containing information about the node configuration 78 | /// 79 | [ConfigurationProperty("node", IsRequired = false)] 80 | public NodeSettings ClusterNode 81 | { 82 | get { return (NodeSettings)base["node"]; } 83 | set { base["node"] = value; } 84 | } 85 | 86 | /// 87 | /// Class containing information about the poller configuration 88 | /// 89 | [ConfigurationProperty("poller", IsRequired = false)] 90 | public PollerSettings ClusterPoller 91 | { 92 | get { return (PollerSettings)base["poller"]; } 93 | set { base["poller"] = value; } 94 | } 95 | 96 | /// 97 | /// Endpoint that contains the hostname and port for auto discovery 98 | /// 99 | public class Endpoint : ConfigurationElement 100 | { 101 | /// 102 | /// The hostname of the cluster containing ".cfg." 103 | /// 104 | [ConfigurationProperty("hostname", IsRequired = true)] 105 | public String HostName 106 | { 107 | get 108 | { 109 | return (String)this["hostname"]; 110 | } 111 | set 112 | { 113 | this["hostname"] = value; 114 | } 115 | } 116 | 117 | /// 118 | /// The port of the endpoint 119 | /// 120 | [ConfigurationProperty("port", IsRequired = true)] 121 | public int Port 122 | { 123 | get 124 | { 125 | return (int)this["port"]; 126 | } 127 | set 128 | { 129 | this["port"] = value; 130 | } 131 | } 132 | } 133 | 134 | /// 135 | /// Settings used for the discovery node 136 | /// 137 | public class NodeSettings : ConfigurationElement 138 | { 139 | /// 140 | /// How many tries the node should use to get a config 141 | /// 142 | [ConfigurationProperty("nodeTries", DefaultValue = -1, IsRequired = false)] 143 | public int NodeTries 144 | { 145 | get { return (int)base["nodeTries"]; } 146 | set { base["nodeTries"] = value; } 147 | } 148 | 149 | /// 150 | /// The delay between tries for the config in miliseconds 151 | /// 152 | [ConfigurationProperty("nodeDelay", DefaultValue = -1, IsRequired = false)] 153 | public int NodeDelay 154 | { 155 | get { return (int)base["nodeDelay"]; } 156 | set { base["nodeDelay"] = value; } 157 | } 158 | } 159 | 160 | /// 161 | /// Settins used for the configuration poller 162 | /// 163 | public class PollerSettings : ConfigurationElement 164 | { 165 | /// 166 | /// The delay between polls in miliseconds 167 | /// 168 | [ConfigurationProperty("intervalDelay", DefaultValue = -1, IsRequired = false)] 169 | public int IntervalDelay 170 | { 171 | get { return (int)base["intervalDelay"]; } 172 | set { base["intervalDelay"] = value; } 173 | } 174 | } 175 | 176 | #endregion 177 | 178 | #region MemcachedConfig 179 | 180 | /// 181 | /// Gets or sets the configuration of the socket pool. 182 | /// 183 | [ConfigurationProperty("socketPool", IsRequired = false)] 184 | public SocketPoolElement SocketPool 185 | { 186 | get { return (SocketPoolElement)base["socketPool"]; } 187 | set { base["socketPool"] = value; } 188 | } 189 | 190 | /// 191 | /// Gets or sets the configuration of the authenticator. 192 | /// 193 | [ConfigurationProperty("authentication", IsRequired = false)] 194 | public AuthenticationElement Authentication 195 | { 196 | get { return (AuthenticationElement)base["authentication"]; } 197 | set { base["authentication"] = value; } 198 | } 199 | 200 | /// 201 | /// Gets or sets the which will be used to assign items to Memcached nodes. 202 | /// 203 | [ConfigurationProperty("locator", IsRequired = false)] 204 | public ProviderElement NodeLocator 205 | { 206 | get { return (ProviderElement)base["locator"]; } 207 | set { base["locator"] = value; } 208 | } 209 | 210 | /// 211 | /// Gets or sets the which will be used to convert item keys for Memcached. 212 | /// 213 | [ConfigurationProperty("keyTransformer", IsRequired = false)] 214 | public ProviderElement KeyTransformer 215 | { 216 | get { return (ProviderElement)base["keyTransformer"]; } 217 | set { base["keyTransformer"] = value; } 218 | } 219 | 220 | /// 221 | /// Gets or sets the which will be used serialzie or deserialize items. 222 | /// 223 | [ConfigurationProperty("transcoder", IsRequired = false)] 224 | public ProviderElement Transcoder 225 | { 226 | get { return (ProviderElement)base["transcoder"]; } 227 | set { base["transcoder"] = value; } 228 | } 229 | 230 | /// 231 | /// Gets or sets the which will be used monitor the performance of the client. 232 | /// 233 | [ConfigurationProperty("performanceMonitor", IsRequired = false)] 234 | public ProviderElement PerformanceMonitor 235 | { 236 | get { return (ProviderElement)base["performanceMonitor"]; } 237 | set { base["performanceMonitor"] = value; } 238 | } 239 | 240 | /// 241 | /// Gets or sets the type of the communication between client and server. 242 | /// 243 | [ConfigurationProperty("protocol", IsRequired = false, DefaultValue = MemcachedProtocol.Binary)] 244 | public MemcachedProtocol Protocol 245 | { 246 | get { return (MemcachedProtocol)base["protocol"]; } 247 | set { base["protocol"] = value; } 248 | } 249 | 250 | #endregion 251 | 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/ConfigurationPoller.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Text; 19 | using System.Timers; 20 | 21 | namespace Amazon.ElastiCacheCluster 22 | { 23 | /// 24 | /// A poller used to reconfigure the client servers when updates occur to the cluster configuration 25 | /// 26 | internal class ConfigurationPoller 27 | { 28 | private static readonly Enyim.Caching.ILog log = Enyim.Caching.LogManager.GetLogger(typeof(ConfigurationPoller)); 29 | 30 | #region Defaults 31 | 32 | // Poll once every minute 33 | private static readonly int DEFAULT_INTERVAL_DELAY = 60000; 34 | 35 | #endregion 36 | 37 | private Timer timer; 38 | private int intervalDelay; 39 | private ElastiCacheClusterConfig config; 40 | 41 | #region Constructors 42 | 43 | /// 44 | /// Creates a poller for Auto Discovery with the default intervals 45 | /// 46 | /// The memcached client to update servers for 47 | public ConfigurationPoller(ElastiCacheClusterConfig config) 48 | : this(config, DEFAULT_INTERVAL_DELAY) { } 49 | 50 | /// 51 | /// Creates a poller for Auto Discovery with the defined itnerval, delay, tries, and try delay for polling 52 | /// 53 | /// The memcached client to update servers for 54 | /// The amount of time between polling operations in miliseconds 55 | public ConfigurationPoller(ElastiCacheClusterConfig config, int intervalDelay) 56 | { 57 | this.intervalDelay = intervalDelay < 0 ? DEFAULT_INTERVAL_DELAY : intervalDelay; 58 | this.config = config; 59 | 60 | this.timer = new Timer(this.intervalDelay); 61 | this.timer.Elapsed += this.pollOnTimedEvent; 62 | } 63 | 64 | #endregion 65 | 66 | #region Polling Methods 67 | 68 | internal void StartTimer() 69 | { 70 | log.Debug("Starting timer"); 71 | this.pollOnTimedEvent(null, null); 72 | this.timer.Start(); 73 | } 74 | 75 | /// 76 | /// Used by the poller's timer to update the cluster configuration if a new version is available 77 | /// 78 | internal void pollOnTimedEvent(Object source, ElapsedEventArgs evnt) 79 | { 80 | log.Debug("Polling..."); 81 | try 82 | { 83 | var oldVersion = config.DiscoveryNode.ClusterVersion; 84 | var endPoints = config.DiscoveryNode.GetEndPointList(); 85 | if (oldVersion != config.DiscoveryNode.ClusterVersion || 86 | (this.config.Pool.nodeLocator != null && endPoints.Count != this.config.Pool.nodeLocator.GetWorkingNodes().Count())) 87 | { 88 | log.DebugFormat("Updating endpoints to have {0} nodes", endPoints.Count); 89 | this.config.Pool.UpdateLocator(endPoints); 90 | } 91 | } 92 | catch(Exception e) 93 | { 94 | try 95 | { 96 | log.Debug("Error updating endpoints, going to attempt to reresolve configuration endpoint.", e); 97 | config.DiscoveryNode.ResolveEndPoint(); 98 | 99 | var oldVersion = config.DiscoveryNode.ClusterVersion; 100 | var endPoints = config.DiscoveryNode.GetEndPointList(); 101 | if (oldVersion != config.DiscoveryNode.ClusterVersion || 102 | (this.config.Pool.nodeLocator != null && endPoints.Count != this.config.Pool.nodeLocator.GetWorkingNodes().Count())) 103 | { 104 | log.DebugFormat("Updating endpoints to have {0} nodes", endPoints.Count); 105 | this.config.Pool.UpdateLocator(endPoints); 106 | } 107 | } 108 | catch (Exception ex) 109 | { 110 | log.Debug("Error updating endpoints. Setting endpoints to empty collection of nodes.", ex); 111 | 112 | /* 113 | * We were not able to retrieve the current node configuration. This is most likely because the application 114 | * is running in development outside of EC2. ElastiCache clusters are only accessible from an EC2 instance 115 | * with the right security permissions. 116 | */ 117 | this.config.Pool.UpdateLocator(new List()); 118 | } 119 | } 120 | } 121 | 122 | #endregion 123 | 124 | /// 125 | /// Disposes the background thread that is used for polling the configs 126 | /// 127 | public void StopPolling() 128 | { 129 | log.Debug("Destroying poller thread"); 130 | if (this.timer != null) 131 | this.timer.Dispose(); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/DiscoveryNode.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Text; 19 | 20 | using System.Net; 21 | using System.Configuration; 22 | using Enyim.Caching.Configuration; 23 | using Enyim.Caching.Memcached; 24 | using Amazon.ElastiCacheCluster.Helpers; 25 | using Amazon.ElastiCacheCluster.Operations; 26 | using Amazon.ElastiCacheCluster.Pools; 27 | using Enyim.Caching.Memcached.Results; 28 | using Enyim.Caching.Memcached.Protocol; 29 | 30 | namespace Amazon.ElastiCacheCluster 31 | { 32 | /// 33 | /// A class that manages the discovery of endpoints inside of an ElastiCache cluster 34 | /// 35 | public class DiscoveryNode 36 | { 37 | #region Static ReadOnlys 38 | 39 | private static readonly Enyim.Caching.ILog log = Enyim.Caching.LogManager.GetLogger(typeof(DiscoveryNode)); 40 | 41 | internal static readonly int DEFAULT_TRY_COUNT = 5; 42 | internal static readonly int DEFAULT_TRY_DELAY = 1000; 43 | 44 | #endregion 45 | 46 | /// 47 | /// The version of memcached running on the Nodes 48 | /// 49 | public Version NodeVersion { get; private set; } 50 | 51 | /// 52 | /// The version of the cluster configuration 53 | /// 54 | public int ClusterVersion { get; private set; } 55 | 56 | /// 57 | /// The number of nodes running inside of the cluster 58 | /// 59 | public int NodesInCluster { get { return this.nodes.Count; } } 60 | 61 | #region Private Fields 62 | 63 | private IPEndPoint EndPoint; 64 | 65 | private IMemcachedNode Node; 66 | 67 | private ElastiCacheClusterConfig config; 68 | 69 | private List nodes = new List(); 70 | 71 | private ConfigurationPoller poller; 72 | 73 | private string hostname; 74 | private int port; 75 | 76 | private int tries; 77 | private int delay; 78 | 79 | private Object nodesLock, endpointLock, clusterLock; 80 | 81 | #endregion 82 | 83 | #region Constructors 84 | 85 | /// 86 | /// The node used to discover endpoints in an ElastiCache cluster 87 | /// 88 | /// The config of the client to access the SocketPool 89 | /// The host name of the cluster with .cfg. in name 90 | /// The port of the cluster 91 | internal DiscoveryNode(ElastiCacheClusterConfig config, string hostname, int port) 92 | : this(config, hostname, port, DEFAULT_TRY_COUNT, DEFAULT_TRY_DELAY) { } 93 | 94 | /// 95 | /// The node used to discover endpoints in an ElastiCache cluster 96 | /// 97 | /// The config of the client to access the SocketPool 98 | /// The host name of the cluster with .cfg. in name 99 | /// The port of the cluster 100 | /// The number of tries for requesting config info 101 | /// The time, in miliseconds, to wait between tries 102 | internal DiscoveryNode(ElastiCacheClusterConfig config, string hostname, int port, int tries, int delay) 103 | { 104 | #region Param Checks 105 | 106 | if (config == null) 107 | throw new ArgumentNullException("config"); 108 | if (string.IsNullOrEmpty(hostname)) 109 | throw new ArgumentNullException("hostname"); 110 | if (port <= 0) 111 | throw new ArgumentException("Port cannot be 0 or less"); 112 | if (tries < 1) 113 | throw new ArgumentException("Must atleast try once"); 114 | if (delay < 0) 115 | throw new ArgumentException("The delay can't be negative"); 116 | if (hostname.IndexOf(".cfg", StringComparison.OrdinalIgnoreCase) < 0) 117 | throw new ArgumentException("The hostname is not able to use Auto Discovery"); 118 | 119 | #endregion 120 | 121 | #region Setting Members 122 | 123 | this.hostname = hostname; 124 | this.port = port; 125 | this.config = config; 126 | this.ClusterVersion = 0; 127 | this.tries = tries; 128 | this.delay = delay; 129 | 130 | this.clusterLock = new Object(); 131 | this.endpointLock = new Object(); 132 | this.nodesLock = new Object(); 133 | 134 | #endregion 135 | 136 | this.ResolveEndPoint(); 137 | } 138 | 139 | #endregion 140 | 141 | #region Poller Methods 142 | 143 | /// 144 | /// Used to start a poller that checks for changes in the cluster client configuration 145 | /// 146 | internal void StartPoller() 147 | { 148 | this.config.Pool.UpdateLocator(new List(new IPEndPoint[] { this.EndPoint })); 149 | this.poller = new ConfigurationPoller(this.config); 150 | this.poller.StartTimer(); 151 | } 152 | 153 | /// 154 | /// Used to start a poller that checks for changes in the cluster client configuration 155 | /// 156 | /// Time between pollings, in miliseconds 157 | internal void StartPoller(int intervalDelay) 158 | { 159 | this.poller = new ConfigurationPoller(this.config, intervalDelay); 160 | this.poller.StartTimer(); 161 | } 162 | 163 | #endregion 164 | 165 | #region Config Info 166 | /// 167 | /// Parses the string NodeConfig into a list of IPEndPoints for configuration 168 | /// 169 | /// A list of IPEndPoints for config to use 170 | internal List GetEndPointList() 171 | { 172 | try 173 | { 174 | var endpoints = AddrUtil.HashEndPointList(this.GetNodeConfig()); 175 | 176 | lock (nodesLock) 177 | { 178 | var nodesToRemove = new HashSet(); 179 | foreach (var node in this.nodes) 180 | { 181 | if (!endpoints.Contains(node.EndPoint)) 182 | nodesToRemove.Add(node); 183 | } 184 | foreach (var node in nodesToRemove) 185 | { 186 | this.nodes.Remove(node); 187 | } 188 | 189 | foreach (var point in endpoints) 190 | { 191 | if (this.nodes.FirstOrDefault(x => x.EndPoint.Equals(point)) == null) 192 | { 193 | this.nodes.Add(this.config.nodeFactory.CreateNode(point, this.config.SocketPool)); 194 | } 195 | } 196 | } 197 | 198 | return endpoints; 199 | } 200 | catch (Exception ex) 201 | { 202 | // Error getting the list of endpoints. Most likely this is due to the 203 | // client being used outside of EC2. 204 | log.Debug("Error getting endpoints list", ex); 205 | throw; 206 | } 207 | } 208 | 209 | /// 210 | /// Gets the Node configuration from "config get cluster" if it's new or "get AmazonElastiCache:cluster" if it's older than 211 | /// 1.4.14 212 | /// 213 | /// A string in the format "hostname1|ip1|port1 hostname2|ip2|port2 ..." 214 | internal string GetNodeConfig() 215 | { 216 | var tries = this.tries; 217 | var nodeVersion = this.GetNodeVersion(); 218 | var older = new Version("1.4.14"); 219 | var waiting = true; 220 | string message = ""; 221 | string[] items = null; 222 | 223 | IGetOperation command = nodeVersion.CompareTo(older) < 0 ? 224 | command = new GetOperation("AmazonElastiCache:cluster") : 225 | command = new ConfigGetOperation("cluster"); 226 | 227 | while (waiting && tries > 0) 228 | { 229 | tries--; 230 | try 231 | { 232 | lock (nodesLock) 233 | { 234 | // This avoids timing out from requesting the config from the endpoint 235 | foreach (var node in this.nodes.ToArray()) 236 | { 237 | try 238 | { 239 | var result = node.Execute(command); 240 | 241 | if (result.Success) 242 | { 243 | var configCommand = command as IConfigOperation; 244 | items = Encoding.UTF8.GetString(configCommand.ConfigResult.Data.Array, configCommand.ConfigResult.Data.Offset, configCommand.ConfigResult.Data.Count).Split('\n'); 245 | waiting = false; 246 | break; 247 | } 248 | else 249 | { 250 | message = result.Message; 251 | } 252 | } 253 | catch (Exception ex) 254 | { 255 | message = ex.Message; 256 | } 257 | } 258 | } 259 | 260 | if (waiting) 261 | System.Threading.Thread.Sleep(this.delay); 262 | 263 | } 264 | catch (Exception ex) 265 | { 266 | message = ex.Message; 267 | System.Threading.Thread.Sleep(this.delay); 268 | } 269 | } 270 | 271 | if (waiting) 272 | { 273 | throw new TimeoutException(String.Format("Could not get config of version " + this.NodeVersion.ToString() + ". Tries: {0} Delay: {1}. " + message, this.tries, this.delay)); 274 | } 275 | 276 | lock (clusterLock) 277 | { 278 | if (this.ClusterVersion < Convert.ToInt32(items[0])) 279 | this.ClusterVersion = Convert.ToInt32(items[0]); 280 | } 281 | return items[1]; 282 | } 283 | 284 | /// 285 | /// Finds the version of Memcached the Elasticache setup is running on 286 | /// 287 | /// Version of memcahced running on nodes 288 | internal Version GetNodeVersion() 289 | { 290 | if (this.NodeVersion != null) 291 | { 292 | return this.NodeVersion; 293 | } 294 | 295 | if (!string.IsNullOrEmpty(this.Node.ToString()) && this.Node.ToString().Equals("TestingAWSInternal")) 296 | { 297 | this.NodeVersion = new Version("1.4.14"); 298 | return this.NodeVersion; 299 | } 300 | 301 | IStatsOperation statcommand = new Enyim.Caching.Memcached.Protocol.Text.StatsOperation(null); 302 | var statresult = this.Node.Execute(statcommand); 303 | 304 | string version; 305 | if (statcommand.Result != null && statcommand.Result.TryGetValue("version", out version)) 306 | { 307 | this.NodeVersion = new Version(version); 308 | return this.NodeVersion; 309 | } 310 | else 311 | { 312 | log.Error("Could not call stats on Node endpoint"); 313 | throw new CommandNotSupportedException("The node does not have a version in stats."); 314 | } 315 | } 316 | 317 | /// 318 | /// Tries to resolve the endpoint ip, used if the connection fails 319 | /// 320 | /// The resolved endpoint as an ip and port 321 | internal IPEndPoint ResolveEndPoint() 322 | { 323 | IPHostEntry entry = null; 324 | var waiting = true; 325 | var tryCount = this.tries; 326 | string message = ""; 327 | 328 | while (tryCount > 0 && waiting) 329 | { 330 | try 331 | { 332 | tryCount--; 333 | entry = Dns.GetHostEntry(hostname); 334 | if (entry.AddressList.Length > 0) 335 | { 336 | waiting = false; 337 | } 338 | } 339 | catch (Exception ex) 340 | { 341 | message = ex.Message; 342 | System.Threading.Thread.Sleep(this.delay); 343 | } 344 | } 345 | 346 | 347 | if (waiting || entry == null) 348 | { 349 | log.Error("Could not resolve hostname to ip"); 350 | throw new TimeoutException(String.Format("Could not resolve hostname to Ip after trying the specified amount: {0}. " + message, this.tries)); 351 | } 352 | 353 | log.DebugFormat("Resolved configuration endpoint {0} to {1}.", hostname, entry.AddressList[0]); 354 | 355 | lock (endpointLock) 356 | { 357 | this.EndPoint = new IPEndPoint(entry.AddressList[0], port); 358 | } 359 | 360 | lock (nodesLock) 361 | { 362 | if (this.Node != null) 363 | { 364 | try 365 | { 366 | this.Node.Dispose(); 367 | } 368 | catch { } 369 | } 370 | this.Node = this.config.nodeFactory.CreateNode(this.EndPoint, this.config.SocketPool); 371 | this.nodes.Clear(); 372 | this.nodes.Add(this.Node); 373 | } 374 | return this.EndPoint; 375 | } 376 | 377 | #endregion 378 | 379 | /// 380 | /// Stops the current poller 381 | /// 382 | public void Dispose() 383 | { 384 | if (this.poller != null) 385 | this.poller.StopPolling(); 386 | } 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/ElastiCacheClusterConfig.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Portions copyright 2010 Attila Kiskó, enyim.com. Please see LICENSE.txt 5 | * for applicable license terms and NOTICE.txt for applicable notices. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"). 8 | * You may not use this file except in compliance with the License. 9 | * A copy of the License is located at 10 | * 11 | * http://aws.amazon.com/apache2.0 12 | * 13 | * or in the "license" file accompanying this file. This file is distributed 14 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 15 | * express or implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | */ 18 | using System; 19 | using System.Collections.Generic; 20 | using System.Net; 21 | using Enyim.Caching.Memcached; 22 | using Enyim.Reflection; 23 | using Enyim.Caching.Memcached.Protocol.Binary; 24 | using Enyim.Caching.Configuration; 25 | using Amazon.ElastiCacheCluster.Pools; 26 | using Amazon.ElastiCacheCluster.Factories; 27 | using System.Configuration; 28 | 29 | namespace Amazon.ElastiCacheCluster 30 | { 31 | /// 32 | /// Configuration class for auto discovery 33 | /// 34 | public class ElastiCacheClusterConfig : IMemcachedClientConfiguration 35 | { 36 | // these are lazy initialized in the getters 37 | private Type nodeLocator; 38 | private ITranscoder transcoder; 39 | private IMemcachedKeyTransformer keyTransformer; 40 | 41 | internal ClusterConfigSettings setup; 42 | internal AutoServerPool Pool; 43 | internal IConfigNodeFactory nodeFactory; 44 | 45 | /// 46 | /// The node used to check the cluster's configuration 47 | /// 48 | public DiscoveryNode DiscoveryNode { get; private set; } 49 | 50 | #region Constructors 51 | 52 | /// 53 | /// Initializes a MemcahcedClient config with auto discovery enabled from the app.config clusterclient section 54 | /// 55 | public ElastiCacheClusterConfig() 56 | : this(null as ClusterConfigSettings) { } 57 | 58 | /// 59 | /// Initializes a MemcahcedClient config with auto discovery enabled from the app.config with the specified section 60 | /// 61 | /// The section to get config settings from 62 | public ElastiCacheClusterConfig(string section) 63 | : this(ConfigurationManager.GetSection(section) as ClusterConfigSettings) { } 64 | 65 | /// 66 | /// Initializes a MemcahcedClient config with auto discovery enabled 67 | /// 68 | /// The hostname of the cluster containing ".cfg." 69 | /// The port to connect to for communication 70 | public ElastiCacheClusterConfig(string hostname, int port) 71 | : this(new ClusterConfigSettings(hostname, port)) { } 72 | 73 | /// 74 | /// Initializes a MemcahcedClient config with auto discovery enabled using the setup provided 75 | /// 76 | /// The setup to get conifg settings from 77 | public ElastiCacheClusterConfig(ClusterConfigSettings setup) 78 | { 79 | if (setup == null) 80 | { 81 | try 82 | { 83 | setup = ConfigurationManager.GetSection("clusterclient") as ClusterConfigSettings; 84 | if (setup == null) 85 | { 86 | throw new ConfigurationErrorsException("Could not instantiate from app.config, setup was null"); 87 | } 88 | } 89 | catch (Exception ex) 90 | { 91 | throw new ConfigurationErrorsException("Could not instantiate from app.config\n" + ex.Message); 92 | } 93 | } 94 | 95 | if (setup.ClusterEndPoint == null) 96 | throw new ArgumentException("Cluster Settings are null"); 97 | if (String.IsNullOrEmpty(setup.ClusterEndPoint.HostName)) 98 | throw new ArgumentNullException("hostname"); 99 | if (setup.ClusterEndPoint.Port <= 0) 100 | throw new ArgumentException("Port cannot be 0 or less"); 101 | 102 | this.setup = setup; 103 | this.Servers = new List(); 104 | 105 | this.Protocol = setup.Protocol; 106 | 107 | if (setup.KeyTransformer == null) 108 | this.KeyTransformer = new DefaultKeyTransformer(); 109 | else 110 | this.KeyTransformer = setup.KeyTransformer.CreateInstance() ?? new DefaultKeyTransformer(); 111 | 112 | this.SocketPool = (ISocketPoolConfiguration)setup.SocketPool ?? new SocketPoolConfiguration(); 113 | this.Authentication = (IAuthenticationConfiguration)setup.Authentication ?? new AuthenticationConfiguration(); 114 | 115 | this.nodeFactory = setup.NodeFactory ?? new DefaultConfigNodeFactory(); 116 | this.nodeLocator = setup.NodeLocator != null ? setup.NodeLocator.Type : typeof(DefaultNodeLocator); 117 | 118 | if (setup.Transcoder != null) 119 | { 120 | this.transcoder = setup.Transcoder.CreateInstance() ?? new DefaultTranscoder(); 121 | } 122 | 123 | if (setup.ClusterEndPoint.HostName.IndexOf(".cfg", StringComparison.OrdinalIgnoreCase) >= 0) 124 | { 125 | if (setup.ClusterNode != null) 126 | { 127 | var _tries = setup.ClusterNode.NodeTries > 0 ? setup.ClusterNode.NodeTries : DiscoveryNode.DEFAULT_TRY_COUNT; 128 | var _delay = setup.ClusterNode.NodeDelay >= 0 ? setup.ClusterNode.NodeDelay : DiscoveryNode.DEFAULT_TRY_DELAY; 129 | this.DiscoveryNode = new DiscoveryNode(this, setup.ClusterEndPoint.HostName, setup.ClusterEndPoint.Port, _tries, _delay); 130 | } 131 | else 132 | this.DiscoveryNode = new DiscoveryNode(this, setup.ClusterEndPoint.HostName, setup.ClusterEndPoint.Port); 133 | } 134 | else 135 | { 136 | throw new ArgumentException("The provided endpoint does not support auto discovery"); 137 | } 138 | } 139 | 140 | #endregion 141 | 142 | #region Members 143 | 144 | /// 145 | /// Gets a list of each representing a Memcached server in the pool. 146 | /// 147 | public IList Servers { get; private set; } 148 | 149 | /// 150 | /// Gets the configuration of the socket pool. 151 | /// 152 | public ISocketPoolConfiguration SocketPool { get; private set; } 153 | 154 | /// 155 | /// Gets the authentication settings. 156 | /// 157 | public IAuthenticationConfiguration Authentication { get; set; } 158 | 159 | /// 160 | /// Gets or sets the which will be used to convert item keys for Memcached. 161 | /// 162 | public IMemcachedKeyTransformer KeyTransformer 163 | { 164 | get { return this.keyTransformer ?? (this.keyTransformer = new DefaultKeyTransformer()); } 165 | set { this.keyTransformer = value; } 166 | } 167 | 168 | /// 169 | /// Gets or sets the Type of the which will be used to assign items to Memcached nodes. 170 | /// 171 | /// If both and are assigned then the latter takes precedence. 172 | public Type NodeLocator 173 | { 174 | get { return this.nodeLocator; } 175 | set 176 | { 177 | ConfigurationHelper.CheckForInterface(value, typeof(IMemcachedNodeLocator)); 178 | this.nodeLocator = value; 179 | } 180 | } 181 | 182 | /// 183 | /// Gets or sets the NodeLocatorFactory instance which will be used to create a new IMemcachedNodeLocator instances. 184 | /// 185 | /// If both and are assigned then the latter takes precedence. 186 | public IProviderFactory NodeLocatorFactory { get; set; } 187 | 188 | /// 189 | /// Gets or sets the which will be used serialize or deserialize items. 190 | /// 191 | public ITranscoder Transcoder 192 | { 193 | get { return this.transcoder ?? (this.transcoder = new DefaultTranscoder()); } 194 | set { this.transcoder = value; } 195 | } 196 | 197 | /// 198 | /// Gets or sets the instance which will be used monitor the performance of the client. 199 | /// 200 | public IPerformanceMonitor PerformanceMonitor { get; set; } 201 | 202 | /// 203 | /// Gets or sets the type of the communication between client and server. 204 | /// 205 | public MemcachedProtocol Protocol { get; set; } 206 | 207 | #endregion 208 | 209 | #region [ interface ] 210 | 211 | IList IMemcachedClientConfiguration.Servers 212 | { 213 | get { return this.Servers; } 214 | } 215 | 216 | ISocketPoolConfiguration IMemcachedClientConfiguration.SocketPool 217 | { 218 | get { return this.SocketPool; } 219 | } 220 | 221 | IAuthenticationConfiguration IMemcachedClientConfiguration.Authentication 222 | { 223 | get { return this.Authentication; } 224 | } 225 | 226 | IMemcachedKeyTransformer IMemcachedClientConfiguration.CreateKeyTransformer() 227 | { 228 | return this.KeyTransformer; 229 | } 230 | 231 | IMemcachedNodeLocator IMemcachedClientConfiguration.CreateNodeLocator() 232 | { 233 | var f = this.NodeLocatorFactory; 234 | if (f != null) return f.Create(); 235 | 236 | return this.NodeLocator == null 237 | ? new DefaultNodeLocator() 238 | : (IMemcachedNodeLocator)FastActivator.Create(this.NodeLocator); 239 | } 240 | 241 | ITranscoder IMemcachedClientConfiguration.CreateTranscoder() 242 | { 243 | return this.Transcoder; 244 | } 245 | 246 | IServerPool IMemcachedClientConfiguration.CreatePool() 247 | { 248 | switch (this.Protocol) 249 | { 250 | case MemcachedProtocol.Text: 251 | this.Pool = new AutoServerPool(this, new Enyim.Caching.Memcached.Protocol.Text.TextOperationFactory()); 252 | break; 253 | case MemcachedProtocol.Binary: 254 | this.Pool = new AutoBinaryPool(this); 255 | break; 256 | default: 257 | throw new ArgumentOutOfRangeException("Unknown protocol: " + (int)this.Protocol); 258 | } 259 | 260 | return this.Pool; 261 | } 262 | 263 | IPerformanceMonitor IMemcachedClientConfiguration.CreatePerformanceMonitor() 264 | { 265 | return this.PerformanceMonitor; 266 | } 267 | 268 | #endregion 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/Factories/DefaultConfigNodeFactory.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | using Enyim.Caching.Configuration; 16 | using Enyim.Caching.Memcached; 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Net; 21 | using System.Text; 22 | 23 | namespace Amazon.ElastiCacheCluster.Factories 24 | { 25 | internal class DefaultConfigNodeFactory : IConfigNodeFactory 26 | { 27 | public IMemcachedNode CreateNode(IPEndPoint endpoint, ISocketPoolConfiguration config) 28 | { 29 | return new MemcachedNode(endpoint, config); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/Factories/IConfigNodeFactory.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | using Enyim.Caching.Configuration; 16 | using Enyim.Caching.Memcached; 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Linq; 20 | using System.Net; 21 | using System.Text; 22 | 23 | namespace Amazon.ElastiCacheCluster.Factories 24 | { 25 | public interface IConfigNodeFactory 26 | { 27 | IMemcachedNode CreateNode(IPEndPoint endpoint, ISocketPoolConfiguration config); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/Helpers/AddrUtil.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Net; 19 | using System.Text; 20 | 21 | namespace Amazon.ElastiCacheCluster.Helpers 22 | { 23 | /// 24 | /// A class used to parse configs of Auto Discovery 25 | /// 26 | internal static class AddrUtil 27 | { 28 | /// 29 | /// Creates a list of endpoints from a string returned in the config request 30 | /// 31 | /// Format: host1|ip1|port1 host2|ip2|port2 ... 32 | /// List of the endpoints parsed to ip:port endpoints for connections 33 | public static List HashEndPointList(string endpoints) 34 | { 35 | List list = new List(); 36 | foreach (var node in endpoints.Split(' ')) 37 | { 38 | string[] parts = node.Split('|'); 39 | IPAddress ip; 40 | if (IPAddress.TryParse(parts[1], out ip)) 41 | { 42 | list.Add(new IPEndPoint(ip, Convert.ToInt32(parts[2]))); 43 | } 44 | } 45 | return list; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/Helpers/TextSocketHelper.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/elasticache-cluster-config-net/30996c7f37dbb978f7d25f926847852a1d6ea50f/Amazon.ElastiCacheCluster/Helpers/TextSocketHelper.cs -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/Operations/ConfigGetOperation.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | using Enyim.Caching.Memcached.Protocol; 16 | using Enyim.Caching.Memcached.Protocol.Text; 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Globalization; 20 | using System.Linq; 21 | using System.Text; 22 | using Enyim.Caching.Memcached.Results; 23 | using Enyim.Caching.Memcached.Results.Extensions; 24 | using Enyim.Caching.Memcached; 25 | using Amazon.ElastiCacheCluster.Helpers; 26 | 27 | namespace Amazon.ElastiCacheCluster.Operations 28 | { 29 | /// 30 | /// Used to get auto discovery information from ElastiCache endpoints version 1.4.14 or higher 31 | /// 32 | internal class ConfigGetOperation : SingleItemOperation, IGetOperation, IConfigOperation 33 | { 34 | private CacheItem result; 35 | 36 | /// 37 | /// Creates a config get for ElastiCache 38 | /// 39 | /// 40 | public ConfigGetOperation(string key) : base(key) { } 41 | 42 | protected override IList> GetBuffer() 43 | { 44 | var command = "config get " + this.Key + TextSocketHelper.CommandTerminator; 45 | 46 | return TextSocketHelper.GetCommandBuffer(command); 47 | } 48 | 49 | protected override Enyim.Caching.Memcached.Results.IOperationResult ReadResponse(PooledSocket socket) 50 | { 51 | string description = TextSocketHelper.ReadResponse(socket); 52 | 53 | if (String.Compare(description, "END", StringComparison.Ordinal) == 0) 54 | return null; 55 | 56 | if (description.Length < 7 || String.Compare(description, 0, "CONFIG ", 0, 7, StringComparison.Ordinal) != 0) 57 | throw new MemcachedClientException("No CONFIG response received.\r\n" + description); 58 | 59 | string[] parts = description.Split(' '); 60 | 61 | /****** Format ******** 62 | * 63 | * CONFIG 64 | * 0 1 2 3 65 | * 66 | */ 67 | 68 | ushort flags = UInt16.Parse(parts[2], CultureInfo.InvariantCulture); 69 | int length = Int32.Parse(parts[3], CultureInfo.InvariantCulture); 70 | 71 | byte[] allNodes = new byte[length]; 72 | byte[] eod = new byte[2]; 73 | 74 | socket.Read(allNodes, 0, length); 75 | socket.Read(eod, 0, 2); // data is terminated by \r\n 76 | 77 | this.result = new CacheItem(flags, new ArraySegment(allNodes, 0, length)); 78 | this.ConfigResult = this.result; 79 | 80 | string response = TextSocketHelper.ReadResponse(socket); 81 | 82 | if (String.Compare(response, "END", StringComparison.Ordinal) != 0) 83 | throw new MemcachedClientException("No END was received."); 84 | 85 | var result = new TextOperationResult(); 86 | return result.Pass(); 87 | } 88 | 89 | protected override bool ReadResponseAsync(PooledSocket socket, Action next) 90 | { 91 | throw new System.NotSupportedException(); 92 | } 93 | 94 | /// 95 | /// The CacheItem result of a "config get *key*" request 96 | /// 97 | public CacheItem Result 98 | { 99 | get { return result; } 100 | } 101 | 102 | public CacheItem ConfigResult { get; set; } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/Operations/GetHelper.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/elasticache-cluster-config-net/30996c7f37dbb978f7d25f926847852a1d6ea50f/Amazon.ElastiCacheCluster/Operations/GetHelper.cs -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/Operations/GetOperation.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/elasticache-cluster-config-net/30996c7f37dbb978f7d25f926847852a1d6ea50f/Amazon.ElastiCacheCluster/Operations/GetOperation.cs -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/Operations/IConfigOperation.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Text; 19 | using Enyim.Caching.Memcached; 20 | 21 | namespace Amazon.ElastiCacheCluster.Operations 22 | { 23 | public interface IConfigOperation 24 | { 25 | CacheItem ConfigResult { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/Pools/AutoBinaryPool.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Portions copyright 2010 Attila Kiskó, enyim.com. Please see LICENSE.txt 5 | * for applicable license terms and NOTICE.txt for applicable notices. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"). 8 | * You may not use this file except in compliance with the License. 9 | * A copy of the License is located at 10 | * 11 | * http://aws.amazon.com/apache2.0 12 | * 13 | * or in the "license" file accompanying this file. This file is distributed 14 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 15 | * express or implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | */ 18 | using System; 19 | using System.Collections.Generic; 20 | using System.Diagnostics; 21 | using System.Net; 22 | using System.Threading; 23 | using Enyim.Caching.Configuration; 24 | using Enyim.Collections; 25 | using System.Security; 26 | using Enyim.Caching.Memcached; 27 | using Enyim.Caching.Memcached.Protocol.Binary; 28 | 29 | namespace Amazon.ElastiCacheCluster.Pools 30 | { 31 | /// 32 | /// Server pool implementing the binary protocol. 33 | /// 34 | internal class AutoBinaryPool : AutoServerPool 35 | { 36 | ISaslAuthenticationProvider authenticationProvider; 37 | IMemcachedClientConfiguration configuration; 38 | 39 | public AutoBinaryPool(IMemcachedClientConfiguration configuration) 40 | : base(configuration, new BinaryOperationFactory()) 41 | { 42 | this.authenticationProvider = GetProvider(configuration); 43 | this.configuration = configuration; 44 | } 45 | 46 | protected override IMemcachedNode CreateNode(IPEndPoint endpoint) 47 | { 48 | if (endpoint == null) 49 | throw new ArgumentNullException("endpoint"); 50 | return new BinaryNode(endpoint, this.configuration.SocketPool, this.authenticationProvider); 51 | } 52 | 53 | private static ISaslAuthenticationProvider GetProvider(IMemcachedClientConfiguration configuration) 54 | { 55 | // create&initialize the authenticator, if any 56 | // we'll use this single instance everywhere, so it must be thread safe 57 | IAuthenticationConfiguration auth = configuration.Authentication; 58 | if (auth != null) 59 | { 60 | Type t = auth.Type; 61 | var provider = (t == null) ? null : Enyim.Reflection.FastActivator.Create(t) as ISaslAuthenticationProvider; 62 | 63 | if (provider != null) 64 | { 65 | provider.Initialize(auth.Parameters); 66 | return provider; 67 | } 68 | } 69 | 70 | return null; 71 | } 72 | 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/Pools/AutoServerPool.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Portions copyright 2010 Attila Kiskó, enyim.com. Please see LICENSE.txt 5 | * for applicable license terms and NOTICE.txt for applicable notices. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"). 8 | * You may not use this file except in compliance with the License. 9 | * A copy of the License is located at 10 | * 11 | * http://aws.amazon.com/apache2.0 12 | * 13 | * or in the "license" file accompanying this file. This file is distributed 14 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 15 | * express or implied. See the License for the specific language governing 16 | * permissions and limitations under the License. 17 | */ 18 | using System; 19 | using System.Linq; 20 | using System.Collections.Generic; 21 | using System.Collections.ObjectModel; 22 | using System.Net; 23 | using System.Threading; 24 | using Enyim.Caching.Configuration; 25 | using Enyim.Caching.Memcached; 26 | 27 | namespace Amazon.ElastiCacheCluster.Pools 28 | { 29 | /// 30 | /// A server pool just like the default that enables safely changing the servers of the locator 31 | /// 32 | internal class AutoServerPool : IServerPool, IDisposable 33 | { 34 | private static readonly Enyim.Caching.ILog log = Enyim.Caching.LogManager.GetLogger(typeof(DefaultServerPool)); 35 | 36 | private IMemcachedNode[] allNodes; 37 | 38 | private IMemcachedClientConfiguration configuration; 39 | private IOperationFactory factory; 40 | internal IMemcachedNodeLocator nodeLocator; 41 | 42 | private object DeadSync = new Object(); 43 | private System.Threading.Timer resurrectTimer; 44 | private bool isTimerActive; 45 | private long deadTimeoutMsec; 46 | private bool isDisposed; 47 | private event Action nodeFailed; 48 | 49 | /// 50 | /// Creates a server pool for auto discovery 51 | /// 52 | /// The client configuration using the pool 53 | /// The factory used to create operations on demand 54 | public AutoServerPool(IMemcachedClientConfiguration configuration, IOperationFactory opFactory) 55 | { 56 | if (configuration == null) throw new ArgumentNullException("socketConfig"); 57 | if (opFactory == null) throw new ArgumentNullException("opFactory"); 58 | 59 | this.configuration = configuration; 60 | this.factory = opFactory; 61 | 62 | this.deadTimeoutMsec = (long)this.configuration.SocketPool.DeadTimeout.TotalMilliseconds; 63 | } 64 | 65 | ~AutoServerPool() 66 | { 67 | try { ((IDisposable)this).Dispose(); } 68 | catch { } 69 | } 70 | 71 | protected virtual IMemcachedNode CreateNode(IPEndPoint endpoint) 72 | { 73 | return new MemcachedNode(endpoint, this.configuration.SocketPool); 74 | } 75 | 76 | private void rezCallback(object state) 77 | { 78 | var isDebug = log.IsDebugEnabled; 79 | 80 | if (isDebug) log.Debug("Checking the dead servers."); 81 | 82 | // how this works: 83 | // 1. timer is created but suspended 84 | // 2. Locate encounters a dead server, so it starts the timer which will trigger after deadTimeout has elapsed 85 | // 3. if another server goes down before the timer is triggered, nothing happens in Locate (isRunning == true). 86 | // however that server will be inspected sooner than Dead Timeout. 87 | // S1 died S2 died dead timeout 88 | // |----*--------*------------*- 89 | // | | 90 | // timer start both servers are checked here 91 | // 4. we iterate all the servers and record it in another list 92 | // 5. if we found a dead server whihc responds to Ping(), the locator will be reinitialized 93 | // 6. if at least one server is still down (Ping() == false), we restart the timer 94 | // 7. if all servers are up, we set isRunning to false, so the timer is suspended 95 | // 8. GOTO 2 96 | lock (this.DeadSync) 97 | { 98 | if (this.isDisposed) 99 | { 100 | if (log.IsWarnEnabled) log.Warn("IsAlive timer was triggered but the pool is already disposed. Ignoring."); 101 | 102 | return; 103 | } 104 | 105 | var nodes = this.allNodes; 106 | var aliveList = new List(nodes.Length); 107 | var changed = false; 108 | var deadCount = 0; 109 | 110 | for (var i = 0; i < nodes.Length; i++) 111 | { 112 | var n = nodes[i]; 113 | if (n.IsAlive) 114 | { 115 | if (isDebug) log.DebugFormat("Alive: {0}", n.EndPoint); 116 | 117 | aliveList.Add(n); 118 | } 119 | else 120 | { 121 | if (isDebug) log.DebugFormat("Dead: {0}", n.EndPoint); 122 | 123 | if (n.Ping()) 124 | { 125 | changed = true; 126 | aliveList.Add(n); 127 | 128 | if (isDebug) log.Debug("Ping ok."); 129 | } 130 | else 131 | { 132 | if (isDebug) log.Debug("Still dead."); 133 | 134 | deadCount++; 135 | } 136 | } 137 | } 138 | 139 | // reinit the locator 140 | if (changed) 141 | { 142 | if (isDebug) log.Debug("Reinitializing the locator."); 143 | 144 | this.nodeLocator.Initialize(aliveList); 145 | } 146 | 147 | // stop or restart the timer 148 | if (deadCount == 0) 149 | { 150 | if (isDebug) log.Debug("deadCount == 0, stopping the timer."); 151 | 152 | this.isTimerActive = false; 153 | } 154 | else 155 | { 156 | if (isDebug) log.DebugFormat("deadCount == {0}, starting the timer.", deadCount); 157 | 158 | this.resurrectTimer.Change(this.deadTimeoutMsec, Timeout.Infinite); 159 | } 160 | } 161 | } 162 | 163 | private void NodeFail(IMemcachedNode node) 164 | { 165 | var isDebug = log.IsDebugEnabled; 166 | if (isDebug) log.DebugFormat("Node {0} is dead.", node.EndPoint); 167 | 168 | // the timer is stopped until we encounter the first dead server 169 | // when we have one, we trigger it and it will run after DeadTimeout has elapsed 170 | lock (this.DeadSync) 171 | { 172 | if (this.isDisposed) 173 | { 174 | if (log.IsWarnEnabled) log.Warn("Got a node fail but the pool is already disposed. Ignoring."); 175 | 176 | return; 177 | } 178 | 179 | // bubble up the fail event to the client 180 | var fail = this.nodeFailed; 181 | if (fail != null) 182 | fail(node); 183 | 184 | // re-initialize the locator 185 | var newLocator = this.configuration.CreateNodeLocator(); 186 | newLocator.Initialize(allNodes.Where(n => n.IsAlive).ToArray()); 187 | Interlocked.Exchange(ref this.nodeLocator, newLocator); 188 | 189 | // the timer is stopped until we encounter the first dead server 190 | // when we have one, we trigger it and it will run after DeadTimeout has elapsed 191 | if (!this.isTimerActive) 192 | { 193 | if (isDebug) log.Debug("Starting the recovery timer."); 194 | 195 | if (this.resurrectTimer == null) 196 | this.resurrectTimer = new Timer(this.rezCallback, null, this.deadTimeoutMsec, Timeout.Infinite); 197 | else 198 | this.resurrectTimer.Change(this.deadTimeoutMsec, Timeout.Infinite); 199 | 200 | this.isTimerActive = true; 201 | 202 | if (isDebug) log.Debug("Timer started."); 203 | } 204 | } 205 | } 206 | 207 | #region [ IServerPool ] 208 | 209 | IMemcachedNode IServerPool.Locate(string key) 210 | { 211 | var node = this.nodeLocator.Locate(key); 212 | 213 | return node; 214 | } 215 | 216 | IOperationFactory IServerPool.OperationFactory 217 | { 218 | get { return this.factory; } 219 | } 220 | 221 | IEnumerable IServerPool.GetWorkingNodes() 222 | { 223 | return this.nodeLocator.GetWorkingNodes(); 224 | } 225 | 226 | void IServerPool.Start() 227 | { 228 | this.allNodes = this.configuration.Servers. 229 | Select(ip => 230 | { 231 | var node = this.CreateNode(ip); 232 | node.Failed += this.NodeFail; 233 | 234 | return node; 235 | }). 236 | ToArray(); 237 | 238 | // initialize the locator 239 | var locator = this.configuration.CreateNodeLocator(); 240 | locator.Initialize(allNodes); 241 | 242 | this.nodeLocator = locator; 243 | 244 | var config = this.configuration as ElastiCacheClusterConfig; 245 | if (config.setup.ClusterPoller.IntervalDelay < 0) 246 | config.DiscoveryNode.StartPoller(); 247 | else 248 | config.DiscoveryNode.StartPoller(config.setup.ClusterPoller.IntervalDelay); 249 | } 250 | 251 | event Action IServerPool.NodeFailed 252 | { 253 | add { this.nodeFailed += value; } 254 | remove { this.nodeFailed -= value; } 255 | } 256 | 257 | #endregion 258 | #region [ IDisposable ] 259 | 260 | void IDisposable.Dispose() 261 | { 262 | GC.SuppressFinalize(this); 263 | 264 | lock (this.DeadSync) 265 | { 266 | if (this.isDisposed) return; 267 | 268 | this.isDisposed = true; 269 | 270 | // dispose the locator first, maybe it wants to access 271 | // the nodes one last time 272 | var nd = this.nodeLocator as IDisposable; 273 | if (nd != null) 274 | try { nd.Dispose(); } 275 | catch (Exception e) { if (log.IsErrorEnabled) log.Error(e); } 276 | 277 | this.nodeLocator = null; 278 | 279 | for (var i = 0; i < this.allNodes.Length; i++) 280 | try { this.allNodes[i].Dispose(); } 281 | catch (Exception e) { if (log.IsErrorEnabled) log.Error(e); } 282 | 283 | // stop the timer 284 | if (this.resurrectTimer != null) 285 | using (this.resurrectTimer) 286 | this.resurrectTimer.Change(Timeout.Infinite, Timeout.Infinite); 287 | 288 | this.allNodes = null; 289 | this.resurrectTimer = null; 290 | } 291 | } 292 | 293 | #endregion 294 | 295 | /// 296 | /// Used to update the servers for Auto discovery 297 | /// 298 | /// The connections to all the cluster nodes 299 | public void UpdateLocator(List endPoints) 300 | { 301 | var newLocator = this.configuration.CreateNodeLocator(); 302 | 303 | var nodes = endPoints.Select(ip => 304 | { 305 | var node = this.CreateNode(ip); 306 | node.Failed += this.NodeFail; 307 | 308 | return node; 309 | }).ToArray(); 310 | 311 | var aliveList = new List(nodes.Length); 312 | var deadList = new List(nodes.Length); 313 | foreach (var node in nodes) 314 | { 315 | var result = this.allNodes.Where(n => n.EndPoint.Equals(node.EndPoint)).ToList(); 316 | 317 | if (result.Count > 0 && !result[0].IsAlive) 318 | { 319 | deadList.Add(result[0]); 320 | continue; 321 | } 322 | 323 | aliveList.Add(node); 324 | } 325 | 326 | newLocator.Initialize(aliveList); 327 | 328 | // Retain All Nodes List With IsAlive Status 329 | var allNodesList = new List(nodes.Length); 330 | allNodesList.AddRange(aliveList); 331 | allNodesList.AddRange(deadList); 332 | 333 | this.allNodes = allNodesList.ToArray(); 334 | 335 | Interlocked.Exchange(ref this.nodeLocator, newLocator); 336 | } 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2008-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). You may not use 4 | * this file except in compliance with the License. A copy of the License is located at 5 | * 6 | * http://aws.amazon.com/apache2.0 7 | * 8 | * or in the "license" file accompanying this file. 9 | * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 10 | * CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | * ***************************************************************************** 13 | * __ _ _ ___ 14 | * ( )( \/\/ )/ __) 15 | * /__\ \ / \__ \ 16 | * (_)(_) \/\/ (___/ 17 | * 18 | * 19 | */ 20 | using System.Reflection; 21 | using System.Runtime.CompilerServices; 22 | using System.Runtime.InteropServices; 23 | 24 | // General Information about an assembly is controlled through the following 25 | // set of attributes. Change these attribute values to modify the information 26 | // associated with an assembly. 27 | [assembly: AssemblyTitle("AmazonElastiCacheClusterConfig")] 28 | [assembly: AssemblyDescription("A configuration object to enable auto discovery in Enyim for ElastiCache")] 29 | [assembly: AssemblyConfiguration("")] 30 | [assembly: AssemblyCompany("Amazon.com, Inc")] 31 | [assembly: AssemblyProduct("AmazonElastiCacheClusterConfig")] 32 | [assembly: AssemblyCopyright("Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2014-2016")] 33 | [assembly: AssemblyTrademark("")] 34 | [assembly: AssemblyCulture("")] 35 | 36 | // Setting ComVisible to false makes the types in this assembly not visible 37 | // to COM components. If you need to access a type in this assembly from 38 | // COM, set the ComVisible attribute to true on that type. 39 | [assembly: ComVisible(false)] 40 | 41 | // The following GUID is for the ID of the typelib if this project is exposed to COM 42 | [assembly: Guid("526dcd43-9e27-40fe-aed6-5d72ad69b7cd")] 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.1.0")] 55 | [assembly: AssemblyFileVersion("1.0.1.0")] 56 | -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/awskey.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/elasticache-cluster-config-net/30996c7f37dbb978f7d25f926847852a1d6ea50f/Amazon.ElastiCacheCluster/awskey.snk -------------------------------------------------------------------------------- /Amazon.ElastiCacheCluster/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /ClusterClientAppTester/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /ClusterClientAppTester/ClusterClientAppTester.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {085886E5-3D56-4E41-941A-611EDD2A31D4} 8 | WinExe 9 | Properties 10 | ClusterClientAppTester 11 | ClusterClientAppTester 12 | v3.5 13 | 512 14 | 15 | ..\ 16 | true 17 | 18 | 19 | AnyCPU 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | AnyCPU 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | False 40 | ..\packages\EnyimMemcached.2.12\lib\net35\Enyim.Caching.dll 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | Form 55 | 56 | 57 | Form1.cs 58 | 59 | 60 | 61 | 62 | Form1.cs 63 | 64 | 65 | ResXFileCodeGenerator 66 | Resources.Designer.cs 67 | Designer 68 | 69 | 70 | True 71 | Resources.resx 72 | True 73 | 74 | 75 | 76 | SettingsSingleFileGenerator 77 | Settings.Designer.cs 78 | 79 | 80 | True 81 | Settings.settings 82 | True 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | {1255ebd3-0340-4af6-a5f2-3d97d8709546} 91 | Amazon.ElastiCacheCluster 92 | 93 | 94 | 95 | 96 | 97 | 98 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 99 | 100 | 101 | 102 | 109 | -------------------------------------------------------------------------------- /ClusterClientAppTester/Form1.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ClusterClientAppTester 2 | { 3 | partial class Form1 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.components = new System.ComponentModel.Container(); 32 | this.groupBox1 = new System.Windows.Forms.GroupBox(); 33 | this.ButtonOlder = new System.Windows.Forms.Button(); 34 | this.TextOlder = new System.Windows.Forms.TextBox(); 35 | this.label2 = new System.Windows.Forms.Label(); 36 | this.TextPort = new System.Windows.Forms.TextBox(); 37 | this.label1 = new System.Windows.Forms.Label(); 38 | this.ButtonInstantiate = new System.Windows.Forms.Button(); 39 | this.groupBox2 = new System.Windows.Forms.GroupBox(); 40 | this.label4 = new System.Windows.Forms.Label(); 41 | this.label3 = new System.Windows.Forms.Label(); 42 | this.TextValue = new System.Windows.Forms.TextBox(); 43 | this.TextKey = new System.Windows.Forms.TextBox(); 44 | this.ButtonAdd = new System.Windows.Forms.Button(); 45 | this.groupBox3 = new System.Windows.Forms.GroupBox(); 46 | this.LabelValue = new System.Windows.Forms.Label(); 47 | this.label6 = new System.Windows.Forms.Label(); 48 | this.TextGetKey = new System.Windows.Forms.TextBox(); 49 | this.label5 = new System.Windows.Forms.Label(); 50 | this.ButtonGet = new System.Windows.Forms.Button(); 51 | this.label7 = new System.Windows.Forms.Label(); 52 | this.LabelStatus = new System.Windows.Forms.Label(); 53 | this.ProgressBarStatus = new System.Windows.Forms.ProgressBar(); 54 | this.ButtonExit = new System.Windows.Forms.Button(); 55 | this.ProgressPoller = new System.Windows.Forms.ProgressBar(); 56 | this.label8 = new System.Windows.Forms.Label(); 57 | this.groupBox4 = new System.Windows.Forms.GroupBox(); 58 | this.LabelVersion = new System.Windows.Forms.Label(); 59 | this.TimerPoller = new System.Windows.Forms.Timer(this.components); 60 | this.groupBox1.SuspendLayout(); 61 | this.groupBox2.SuspendLayout(); 62 | this.groupBox3.SuspendLayout(); 63 | this.groupBox4.SuspendLayout(); 64 | this.SuspendLayout(); 65 | // 66 | // groupBox1 67 | // 68 | this.groupBox1.Controls.Add(this.ButtonOlder); 69 | this.groupBox1.Controls.Add(this.TextOlder); 70 | this.groupBox1.Controls.Add(this.label2); 71 | this.groupBox1.Controls.Add(this.TextPort); 72 | this.groupBox1.Controls.Add(this.label1); 73 | this.groupBox1.Controls.Add(this.ButtonInstantiate); 74 | this.groupBox1.Location = new System.Drawing.Point(12, 12); 75 | this.groupBox1.Name = "groupBox1"; 76 | this.groupBox1.Size = new System.Drawing.Size(317, 125); 77 | this.groupBox1.TabIndex = 0; 78 | this.groupBox1.TabStop = false; 79 | this.groupBox1.Text = "Connection Tests"; 80 | // 81 | // ButtonOlder 82 | // 83 | this.ButtonOlder.Location = new System.Drawing.Point(159, 85); 84 | this.ButtonOlder.Name = "ButtonOlder"; 85 | this.ButtonOlder.Size = new System.Drawing.Size(133, 23); 86 | this.ButtonOlder.TabIndex = 7; 87 | this.ButtonOlder.Text = "Instantiate From Params"; 88 | this.ButtonOlder.UseVisualStyleBackColor = true; 89 | this.ButtonOlder.Click += new System.EventHandler(this.ButtonOlder_Click); 90 | // 91 | // TextOlder 92 | // 93 | this.TextOlder.Location = new System.Drawing.Point(6, 48); 94 | this.TextOlder.Name = "TextOlder"; 95 | this.TextOlder.Size = new System.Drawing.Size(172, 20); 96 | this.TextOlder.TabIndex = 6; 97 | this.TextOlder.Text = "versiontesting.4mpstz.cfg.use1.cache.amazonaws.com"; 98 | // 99 | // label2 100 | // 101 | this.label2.AutoSize = true; 102 | this.label2.Location = new System.Drawing.Point(199, 29); 103 | this.label2.Name = "label2"; 104 | this.label2.Size = new System.Drawing.Size(74, 13); 105 | this.label2.TabIndex = 4; 106 | this.label2.Text = "Endpoint Port:"; 107 | // 108 | // TextPort 109 | // 110 | this.TextPort.Location = new System.Drawing.Point(202, 48); 111 | this.TextPort.Name = "TextPort"; 112 | this.TextPort.Size = new System.Drawing.Size(41, 20); 113 | this.TextPort.TabIndex = 3; 114 | this.TextPort.Text = "11211"; 115 | // 116 | // label1 117 | // 118 | this.label1.AutoSize = true; 119 | this.label1.Location = new System.Drawing.Point(6, 29); 120 | this.label1.Name = "label1"; 121 | this.label1.Size = new System.Drawing.Size(83, 13); 122 | this.label1.TabIndex = 2; 123 | this.label1.Text = "Endpoint Name:"; 124 | // 125 | // ButtonInstantiate 126 | // 127 | this.ButtonInstantiate.Location = new System.Drawing.Point(6, 85); 128 | this.ButtonInstantiate.Name = "ButtonInstantiate"; 129 | this.ButtonInstantiate.Size = new System.Drawing.Size(147, 23); 130 | this.ButtonInstantiate.TabIndex = 0; 131 | this.ButtonInstantiate.Text = "Instantiate From App.Config"; 132 | this.ButtonInstantiate.UseVisualStyleBackColor = true; 133 | this.ButtonInstantiate.Click += new System.EventHandler(this.ButtonInstantiate_Click); 134 | // 135 | // groupBox2 136 | // 137 | this.groupBox2.Controls.Add(this.label4); 138 | this.groupBox2.Controls.Add(this.label3); 139 | this.groupBox2.Controls.Add(this.TextValue); 140 | this.groupBox2.Controls.Add(this.TextKey); 141 | this.groupBox2.Controls.Add(this.ButtonAdd); 142 | this.groupBox2.Location = new System.Drawing.Point(335, 12); 143 | this.groupBox2.Name = "groupBox2"; 144 | this.groupBox2.Size = new System.Drawing.Size(235, 125); 145 | this.groupBox2.TabIndex = 1; 146 | this.groupBox2.TabStop = false; 147 | this.groupBox2.Text = "Initial Add"; 148 | // 149 | // label4 150 | // 151 | this.label4.AutoSize = true; 152 | this.label4.Location = new System.Drawing.Point(109, 29); 153 | this.label4.Name = "label4"; 154 | this.label4.Size = new System.Drawing.Size(37, 13); 155 | this.label4.TabIndex = 4; 156 | this.label4.Text = "Value:"; 157 | // 158 | // label3 159 | // 160 | this.label3.AutoSize = true; 161 | this.label3.Location = new System.Drawing.Point(3, 29); 162 | this.label3.Name = "label3"; 163 | this.label3.Size = new System.Drawing.Size(28, 13); 164 | this.label3.TabIndex = 3; 165 | this.label3.Text = "Key:"; 166 | // 167 | // TextValue 168 | // 169 | this.TextValue.Location = new System.Drawing.Point(112, 48); 170 | this.TextValue.Name = "TextValue"; 171 | this.TextValue.Size = new System.Drawing.Size(100, 20); 172 | this.TextValue.TabIndex = 2; 173 | this.TextValue.Text = "Hello World"; 174 | // 175 | // TextKey 176 | // 177 | this.TextKey.Location = new System.Drawing.Point(6, 48); 178 | this.TextKey.Name = "TextKey"; 179 | this.TextKey.Size = new System.Drawing.Size(90, 20); 180 | this.TextKey.TabIndex = 1; 181 | this.TextKey.Text = "Test"; 182 | // 183 | // ButtonAdd 184 | // 185 | this.ButtonAdd.Enabled = false; 186 | this.ButtonAdd.Location = new System.Drawing.Point(6, 96); 187 | this.ButtonAdd.Name = "ButtonAdd"; 188 | this.ButtonAdd.Size = new System.Drawing.Size(89, 23); 189 | this.ButtonAdd.TabIndex = 0; 190 | this.ButtonAdd.Text = "Add to Cache"; 191 | this.ButtonAdd.UseVisualStyleBackColor = true; 192 | this.ButtonAdd.Click += new System.EventHandler(this.ButtonAdd_Click); 193 | // 194 | // groupBox3 195 | // 196 | this.groupBox3.Controls.Add(this.LabelValue); 197 | this.groupBox3.Controls.Add(this.label6); 198 | this.groupBox3.Controls.Add(this.TextGetKey); 199 | this.groupBox3.Controls.Add(this.label5); 200 | this.groupBox3.Controls.Add(this.ButtonGet); 201 | this.groupBox3.Location = new System.Drawing.Point(12, 144); 202 | this.groupBox3.Name = "groupBox3"; 203 | this.groupBox3.Size = new System.Drawing.Size(317, 109); 204 | this.groupBox3.TabIndex = 2; 205 | this.groupBox3.TabStop = false; 206 | this.groupBox3.Text = "Initial Get"; 207 | // 208 | // LabelValue 209 | // 210 | this.LabelValue.AutoSize = true; 211 | this.LabelValue.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 212 | this.LabelValue.Location = new System.Drawing.Point(139, 46); 213 | this.LabelValue.Name = "LabelValue"; 214 | this.LabelValue.Size = new System.Drawing.Size(0, 25); 215 | this.LabelValue.TabIndex = 4; 216 | // 217 | // label6 218 | // 219 | this.label6.AutoSize = true; 220 | this.label6.Location = new System.Drawing.Point(141, 20); 221 | this.label6.Name = "label6"; 222 | this.label6.Size = new System.Drawing.Size(37, 13); 223 | this.label6.TabIndex = 3; 224 | this.label6.Text = "Value:"; 225 | // 226 | // TextGetKey 227 | // 228 | this.TextGetKey.Location = new System.Drawing.Point(9, 37); 229 | this.TextGetKey.Name = "TextGetKey"; 230 | this.TextGetKey.ReadOnly = true; 231 | this.TextGetKey.Size = new System.Drawing.Size(100, 20); 232 | this.TextGetKey.TabIndex = 2; 233 | this.TextGetKey.Text = "Test"; 234 | // 235 | // label5 236 | // 237 | this.label5.AutoSize = true; 238 | this.label5.Location = new System.Drawing.Point(9, 20); 239 | this.label5.Name = "label5"; 240 | this.label5.Size = new System.Drawing.Size(28, 13); 241 | this.label5.TabIndex = 1; 242 | this.label5.Text = "Key:"; 243 | // 244 | // ButtonGet 245 | // 246 | this.ButtonGet.Enabled = false; 247 | this.ButtonGet.Location = new System.Drawing.Point(6, 80); 248 | this.ButtonGet.Name = "ButtonGet"; 249 | this.ButtonGet.Size = new System.Drawing.Size(75, 23); 250 | this.ButtonGet.TabIndex = 0; 251 | this.ButtonGet.Text = "Get Value"; 252 | this.ButtonGet.UseVisualStyleBackColor = true; 253 | this.ButtonGet.Click += new System.EventHandler(this.ButtonGet_Click); 254 | // 255 | // label7 256 | // 257 | this.label7.AutoSize = true; 258 | this.label7.Location = new System.Drawing.Point(12, 275); 259 | this.label7.Name = "label7"; 260 | this.label7.Size = new System.Drawing.Size(40, 13); 261 | this.label7.TabIndex = 3; 262 | this.label7.Text = "Status:"; 263 | // 264 | // LabelStatus 265 | // 266 | this.LabelStatus.AutoSize = true; 267 | this.LabelStatus.Location = new System.Drawing.Point(58, 275); 268 | this.LabelStatus.Name = "LabelStatus"; 269 | this.LabelStatus.Size = new System.Drawing.Size(93, 13); 270 | this.LabelStatus.TabIndex = 4; 271 | this.LabelStatus.Text = "Waiting on action."; 272 | // 273 | // ProgressBarStatus 274 | // 275 | this.ProgressBarStatus.Location = new System.Drawing.Point(11, 300); 276 | this.ProgressBarStatus.Name = "ProgressBarStatus"; 277 | this.ProgressBarStatus.Size = new System.Drawing.Size(140, 23); 278 | this.ProgressBarStatus.TabIndex = 5; 279 | // 280 | // ButtonExit 281 | // 282 | this.ButtonExit.Location = new System.Drawing.Point(495, 300); 283 | this.ButtonExit.Name = "ButtonExit"; 284 | this.ButtonExit.Size = new System.Drawing.Size(75, 23); 285 | this.ButtonExit.TabIndex = 6; 286 | this.ButtonExit.Text = "Exit"; 287 | this.ButtonExit.UseVisualStyleBackColor = true; 288 | this.ButtonExit.Click += new System.EventHandler(this.ButtonExit_Click); 289 | // 290 | // ProgressPoller 291 | // 292 | this.ProgressPoller.Location = new System.Drawing.Point(40, 48); 293 | this.ProgressPoller.Maximum = 60; 294 | this.ProgressPoller.Name = "ProgressPoller"; 295 | this.ProgressPoller.Size = new System.Drawing.Size(157, 23); 296 | this.ProgressPoller.Step = 1; 297 | this.ProgressPoller.TabIndex = 7; 298 | // 299 | // label8 300 | // 301 | this.label8.AutoSize = true; 302 | this.label8.Location = new System.Drawing.Point(40, 29); 303 | this.label8.Name = "label8"; 304 | this.label8.Size = new System.Drawing.Size(145, 13); 305 | this.label8.TabIndex = 8; 306 | this.label8.Text = "Wait 1 minute to check poller"; 307 | // 308 | // groupBox4 309 | // 310 | this.groupBox4.Controls.Add(this.LabelVersion); 311 | this.groupBox4.Controls.Add(this.ProgressPoller); 312 | this.groupBox4.Controls.Add(this.label8); 313 | this.groupBox4.Location = new System.Drawing.Point(336, 144); 314 | this.groupBox4.Name = "groupBox4"; 315 | this.groupBox4.Size = new System.Drawing.Size(234, 109); 316 | this.groupBox4.TabIndex = 9; 317 | this.groupBox4.TabStop = false; 318 | this.groupBox4.Text = "Poller Test"; 319 | // 320 | // LabelVersion 321 | // 322 | this.LabelVersion.AutoSize = true; 323 | this.LabelVersion.Location = new System.Drawing.Point(6, 80); 324 | this.LabelVersion.Name = "LabelVersion"; 325 | this.LabelVersion.Size = new System.Drawing.Size(0, 13); 326 | this.LabelVersion.TabIndex = 9; 327 | // 328 | // TimerPoller 329 | // 330 | this.TimerPoller.Interval = 1000; 331 | this.TimerPoller.Tick += new System.EventHandler(this.TimerPoller_Tick); 332 | // 333 | // Form1 334 | // 335 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 336 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 337 | this.ClientSize = new System.Drawing.Size(582, 335); 338 | this.Controls.Add(this.groupBox4); 339 | this.Controls.Add(this.ButtonExit); 340 | this.Controls.Add(this.ProgressBarStatus); 341 | this.Controls.Add(this.LabelStatus); 342 | this.Controls.Add(this.label7); 343 | this.Controls.Add(this.groupBox3); 344 | this.Controls.Add(this.groupBox2); 345 | this.Controls.Add(this.groupBox1); 346 | this.MaximizeBox = false; 347 | this.Name = "Form1"; 348 | this.ShowIcon = false; 349 | this.Text = "Cluster Client Tests"; 350 | this.groupBox1.ResumeLayout(false); 351 | this.groupBox1.PerformLayout(); 352 | this.groupBox2.ResumeLayout(false); 353 | this.groupBox2.PerformLayout(); 354 | this.groupBox3.ResumeLayout(false); 355 | this.groupBox3.PerformLayout(); 356 | this.groupBox4.ResumeLayout(false); 357 | this.groupBox4.PerformLayout(); 358 | this.ResumeLayout(false); 359 | this.PerformLayout(); 360 | 361 | } 362 | 363 | #endregion 364 | 365 | private System.Windows.Forms.GroupBox groupBox1; 366 | private System.Windows.Forms.Label label2; 367 | private System.Windows.Forms.TextBox TextPort; 368 | private System.Windows.Forms.Label label1; 369 | private System.Windows.Forms.Button ButtonInstantiate; 370 | private System.Windows.Forms.GroupBox groupBox2; 371 | private System.Windows.Forms.Button ButtonAdd; 372 | private System.Windows.Forms.Label label4; 373 | private System.Windows.Forms.Label label3; 374 | private System.Windows.Forms.TextBox TextValue; 375 | private System.Windows.Forms.TextBox TextKey; 376 | private System.Windows.Forms.GroupBox groupBox3; 377 | private System.Windows.Forms.Label LabelValue; 378 | private System.Windows.Forms.Label label6; 379 | private System.Windows.Forms.TextBox TextGetKey; 380 | private System.Windows.Forms.Label label5; 381 | private System.Windows.Forms.Button ButtonGet; 382 | private System.Windows.Forms.Label label7; 383 | private System.Windows.Forms.Label LabelStatus; 384 | private System.Windows.Forms.ProgressBar ProgressBarStatus; 385 | private System.Windows.Forms.TextBox TextOlder; 386 | private System.Windows.Forms.Button ButtonOlder; 387 | private System.Windows.Forms.Button ButtonExit; 388 | private System.Windows.Forms.ProgressBar ProgressPoller; 389 | private System.Windows.Forms.Label label8; 390 | private System.Windows.Forms.GroupBox groupBox4; 391 | private System.Windows.Forms.Timer TimerPoller; 392 | private System.Windows.Forms.Label LabelVersion; 393 | } 394 | } 395 | 396 | -------------------------------------------------------------------------------- /ClusterClientAppTester/Form1.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | using System; 16 | using System.Collections.Generic; 17 | using System.ComponentModel; 18 | using System.Data; 19 | using System.Drawing; 20 | using System.Linq; 21 | using System.Text; 22 | using System.Windows.Forms; 23 | 24 | using Enyim.Caching; 25 | using Amazon.ElastiCacheCluster; 26 | using Enyim.Caching.Memcached; 27 | 28 | namespace ClusterClientAppTester 29 | { 30 | public partial class Form1 : Form 31 | { 32 | private MemcachedClient mem; 33 | private ElastiCacheClusterConfig config; 34 | 35 | public Form1() 36 | { 37 | InitializeComponent(); 38 | } 39 | 40 | public void ErrorAlert(string error) 41 | { 42 | MessageBox.Show(error); 43 | } 44 | 45 | private void ButtonInstantiate_Click(object sender, EventArgs e) 46 | { 47 | try 48 | { 49 | this.LabelStatus.Text = "Instantiating"; 50 | 51 | // Instantiates config from app.config in the clusterclient section 52 | this.config = new ElastiCacheClusterConfig(); 53 | 54 | mem = new MemcachedClient(this.config); 55 | 56 | #region UI Stuff 57 | 58 | this.TimerPoller.Enabled = false; 59 | this.ProgressPoller.Value = 0; 60 | this.TimerPoller.Enabled = true; 61 | 62 | if (mem == null) 63 | { 64 | this.LabelStatus.Text = "MemcachedClient returned a null object"; 65 | } 66 | else 67 | { 68 | this.ButtonAdd.Enabled = true; 69 | this.ButtonGet.Enabled = true; 70 | this.LabelStatus.Text = "Instantiation Success"; 71 | this.ProgressBarStatus.Value = 25; 72 | } 73 | 74 | #endregion 75 | } 76 | catch (Exception ex) 77 | { 78 | this.LabelStatus.Text = ex.Message; 79 | } 80 | } 81 | 82 | private void ButtonOlder_Click(object sender, EventArgs e) 83 | { 84 | try 85 | { 86 | this.LabelStatus.Text = "Instantiating"; 87 | 88 | // Instantiates client with default settings and uses the hostname and port provided 89 | this.config = new ElastiCacheClusterConfig(this.TextOlder.Text, Convert.ToInt32(this.TextPort.Text)); 90 | 91 | this.mem = new MemcachedClient(this.config); 92 | 93 | #region UI Stuff 94 | 95 | this.TimerPoller.Enabled = false; 96 | this.ProgressPoller.Value = 0; 97 | this.TimerPoller.Enabled = true; 98 | 99 | if (mem == null) 100 | { 101 | this.LabelStatus.Text = "MemcachedClient returned a null object"; 102 | } 103 | else 104 | { 105 | this.ButtonAdd.Enabled = true; 106 | this.ButtonGet.Enabled = true; 107 | this.LabelStatus.Text = "Old Instantiation Success"; 108 | this.ProgressBarStatus.Value = 25; 109 | } 110 | 111 | #endregion 112 | } 113 | catch (Exception ex) 114 | { 115 | this.LabelStatus.Text = ex.Message; 116 | } 117 | } 118 | 119 | private void ButtonAdd_Click(object sender, EventArgs e) 120 | { 121 | try 122 | { 123 | this.LabelStatus.Text = "Storing"; 124 | 125 | // Stores the same as an Enyim client, just that the nodes are already set through the config object 126 | if (mem.Store(StoreMode.Set, this.TextKey.Text, this.TextValue.Text)) 127 | { 128 | #region UI Stuff 129 | 130 | this.TextGetKey.Text = this.TextKey.Text; 131 | this.LabelStatus.Text = "Storing Success"; 132 | if (this.ProgressBarStatus.Value < 50) 133 | { 134 | this.ProgressBarStatus.Value = 50; 135 | } 136 | 137 | #endregion 138 | } 139 | else 140 | { 141 | this.LabelStatus.Text = "Failed to store"; 142 | } 143 | } 144 | catch (Exception ex) 145 | { 146 | this.LabelStatus.Text = ex.Message; 147 | } 148 | } 149 | 150 | private void ButtonGet_Click(object sender, EventArgs e) 151 | { 152 | try 153 | { 154 | this.LabelStatus.Text = "Getting"; 155 | 156 | // Gets the value the same way as a normal Enyim client from the dynamic nodes provided from the config 157 | object val; 158 | if (!mem.TryGet(TextGetKey.Text, out val)) 159 | { 160 | 161 | #region UI Stuff 162 | 163 | this.LabelStatus.Text = "Failed to get"; 164 | } 165 | else 166 | { 167 | this.LabelValue.Text = val as string; 168 | this.LabelStatus.Text = "Getting Success"; 169 | if (this.ProgressBarStatus.Value < 75) 170 | { 171 | this.ProgressBarStatus.Value = 75; 172 | } 173 | } 174 | 175 | #endregion 176 | } 177 | catch (Exception ex) 178 | { 179 | this.LabelStatus.Text = ex.Message; 180 | } 181 | } 182 | 183 | private void ButtonExit_Click(object sender, EventArgs e) 184 | { 185 | if (mem != null) 186 | this.mem.Dispose(); 187 | Application.Exit(); 188 | } 189 | 190 | private void TimerPoller_Tick(object sender, EventArgs e) 191 | { 192 | this.LabelVersion.Text = String.Format("Config version is: {0} ", this.config.DiscoveryNode.ClusterVersion) 193 | + String.Format("Number of nodes: {0}", this.config.DiscoveryNode.NodesInCluster); 194 | this.ProgressPoller.PerformStep(); 195 | if (this.ProgressPoller.Value == 60) 196 | { 197 | this.ProgressPoller.Value = 0; 198 | this.ProgressBarStatus.Value = 100; 199 | this.LabelStatus.Text = "Poller cycle completed."; 200 | } 201 | 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /ClusterClientAppTester/Form1.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 17, 17 122 | 123 | 124 | True 125 | 126 | -------------------------------------------------------------------------------- /ClusterClientAppTester/Program.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Windows.Forms; 19 | 20 | namespace ClusterClientAppTester 21 | { 22 | static class Program 23 | { 24 | private static Form1 form; 25 | /// 26 | /// The main entry point for the application. 27 | /// 28 | [STAThread] 29 | static void Main() 30 | { 31 | try 32 | { 33 | Application.EnableVisualStyles(); 34 | Application.SetCompatibleTextRenderingDefault(false); 35 | 36 | AppDomain currentDomain = AppDomain.CurrentDomain; 37 | currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler); 38 | 39 | Program.form = new Form1(); 40 | Application.Run(Program.form); 41 | } 42 | catch (Exception ex) 43 | { 44 | System.IO.File.WriteAllText("log.txt", ex.Message); 45 | Program.form.ErrorAlert(ex.Message); 46 | } 47 | } 48 | 49 | static void MyHandler(object sender, UnhandledExceptionEventArgs args) 50 | { 51 | Exception e = (Exception)args.ExceptionObject; 52 | System.IO.File.WriteAllText("log.txt", e.Message); 53 | Program.form.Name = e.Message; 54 | Program.form.ErrorAlert(e.Message); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ClusterClientAppTester/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ClusterClientAppTester")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ClusterClientAppTester")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("fdd8eaec-ac53-479d-8703-7f84e890701e")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /ClusterClientAppTester/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.18444 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ClusterClientAppTester.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ClusterClientAppTester.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ClusterClientAppTester/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /ClusterClientAppTester/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.18444 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ClusterClientAppTester.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ClusterClientAppTester/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ClusterClientAppTester/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /ElastiCacheClusterConfig.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30626.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.ElastiCacheCluster", "Amazon.ElastiCacheCluster\Amazon.ElastiCacheCluster.csproj", "{1255EBD3-0340-4AF6-A5F2-3D97D8709546}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClusterClientAppTester", "ClusterClientAppTester\ClusterClientAppTester.csproj", "{085886E5-3D56-4E41-941A-611EDD2A31D4}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LocalSimulationTests", "LocalSimulationTests\LocalSimulationTests.csproj", "{4348C13C-0E60-47C2-B86E-8D1FBB4060D5}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{8A597A8A-8879-4589-80C6-3FA1B4BDE518}" 13 | ProjectSection(SolutionItems) = preProject 14 | .nuget\NuGet.Config = .nuget\NuGet.Config 15 | .nuget\NuGet.exe = .nuget\NuGet.exe 16 | .nuget\NuGet.targets = .nuget\NuGet.targets 17 | EndProjectSection 18 | EndProject 19 | Global 20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 21 | Debug|Any CPU = Debug|Any CPU 22 | Release|Any CPU = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {1255EBD3-0340-4AF6-A5F2-3D97D8709546}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {1255EBD3-0340-4AF6-A5F2-3D97D8709546}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {1255EBD3-0340-4AF6-A5F2-3D97D8709546}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {1255EBD3-0340-4AF6-A5F2-3D97D8709546}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {085886E5-3D56-4E41-941A-611EDD2A31D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {085886E5-3D56-4E41-941A-611EDD2A31D4}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {085886E5-3D56-4E41-941A-611EDD2A31D4}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {085886E5-3D56-4E41-941A-611EDD2A31D4}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {4348C13C-0E60-47C2-B86E-8D1FBB4060D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {4348C13C-0E60-47C2-B86E-8D1FBB4060D5}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {4348C13C-0E60-47C2-B86E-8D1FBB4060D5}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {4348C13C-0E60-47C2-B86E-8D1FBB4060D5}.Release|Any CPU.Build.0 = Release|Any CPU 37 | EndGlobalSection 38 | GlobalSection(SolutionProperties) = preSolution 39 | HideSolutionNode = FALSE 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | 4 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 5 | 6 | 1. Definitions. 7 | 8 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 9 | 10 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 11 | 12 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 13 | 14 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 15 | 16 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 17 | 18 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 19 | 20 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 21 | 22 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 23 | 24 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 25 | 26 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 27 | 28 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 29 | 30 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 31 | 32 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 33 | 34 | 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and 35 | 2. You must cause any modified files to carry prominent notices stating that You changed the files; and 36 | 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 37 | 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. 38 | 39 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 40 | 41 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 42 | 43 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 44 | 45 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 46 | 47 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 48 | 49 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 50 | 51 | END OF TERMS AND CONDITIONS 52 | -------------------------------------------------------------------------------- /LocalSimulationTests/ConfigTests.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | using System; 16 | using System.Configuration; 17 | using Microsoft.VisualStudio.TestTools.UnitTesting; 18 | using Amazon.ElastiCacheCluster; 19 | using Enyim.Caching; 20 | 21 | namespace LocalSimulationTests 22 | { 23 | [TestClass] 24 | public class ConfigTests 25 | { 26 | ElastiCacheClusterConfig config; 27 | MemcachedClient client; 28 | 29 | [TestMethod] 30 | public void ConfigTest() 31 | { 32 | // The url below is used to bypass the .cfg. contraint of the hostname for testing locally 33 | ClusterConfigSettings settings = new ClusterConfigSettings("www.cfg.org", 11211); 34 | settings.NodeFactory = new NodeFactory(); 35 | config = new ElastiCacheClusterConfig(settings); 36 | this.config.DiscoveryNode.Dispose(); 37 | } 38 | 39 | [TestMethod] 40 | public void InitialRequestTest() 41 | { 42 | // The url below is used to bypass the .cfg. contraint of the hostname for testing locally 43 | ClusterConfigSettings settings = new ClusterConfigSettings("www.cfg.org", 11211); 44 | settings.NodeFactory = new NodeFactory(); 45 | config = new ElastiCacheClusterConfig(settings); 46 | 47 | client = new MemcachedClient(config); 48 | 49 | Assert.AreEqual(new Version("1.4.14"), config.DiscoveryNode.NodeVersion); 50 | Assert.AreEqual(1, config.DiscoveryNode.ClusterVersion); 51 | Assert.AreEqual(3, config.DiscoveryNode.NodesInCluster); 52 | 53 | this.config.DiscoveryNode.Dispose(); 54 | client.Dispose(); 55 | } 56 | 57 | [TestMethod] 58 | public void PollerTesting() 59 | { 60 | //Poller is set to poll every second to make this test faster 61 | ClusterConfigSettings settings = new ClusterConfigSettings("www.cfg.org", 11211); 62 | settings.NodeFactory = new NodeFactory(); 63 | settings.ClusterPoller.IntervalDelay = 1000; 64 | config = new ElastiCacheClusterConfig(settings); 65 | 66 | client = new MemcachedClient(config); 67 | 68 | // Buffer time to wait, this can fail occasionally because delays can occur in the poller or timer 69 | System.Threading.Thread.Sleep(3000); 70 | Assert.AreEqual(3, config.DiscoveryNode.ClusterVersion); 71 | Assert.AreEqual(1, config.DiscoveryNode.NodesInCluster); 72 | 73 | this.config.DiscoveryNode.Dispose(); 74 | client.Dispose(); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /LocalSimulationTests/LocalSimulationTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | {4348C13C-0E60-47C2-B86E-8D1FBB4060D5} 7 | Library 8 | Properties 9 | LocalSimulationTests 10 | LocalSimulationTests 11 | v3.5 12 | 512 13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 10.0 15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 17 | False 18 | UnitTest 19 | 20 | ..\ 21 | true 22 | 23 | 24 | true 25 | full 26 | false 27 | bin\Debug\ 28 | DEBUG;TRACE 29 | prompt 30 | 4 31 | 32 | 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE 37 | prompt 38 | 4 39 | 40 | 41 | 42 | ..\packages\EnyimMemcached.2.12\lib\net35\Enyim.Caching.dll 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | {1255ebd3-0340-4af6-a5f2-3d97d8709546} 68 | Amazon.ElastiCacheCluster 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | False 79 | 80 | 81 | False 82 | 83 | 84 | False 85 | 86 | 87 | False 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 98 | 99 | 100 | 101 | 108 | -------------------------------------------------------------------------------- /LocalSimulationTests/NodeFactory.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Text; 19 | using Amazon.ElastiCacheCluster.Factories; 20 | using Enyim.Caching.Memcached; 21 | 22 | namespace LocalSimulationTests 23 | { 24 | public class NodeFactory : IConfigNodeFactory 25 | { 26 | TestNode node; 27 | 28 | public NodeFactory() 29 | { 30 | this.node = new TestNode(); 31 | } 32 | 33 | public IMemcachedNode CreateNode(System.Net.IPEndPoint endpoint, Enyim.Caching.Configuration.ISocketPoolConfiguration config) 34 | { 35 | return node; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LocalSimulationTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("LocalSimulationTests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("LocalSimulationTests")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("67675e7a-9380-4882-8b1d-f4345703e8d5")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /LocalSimulationTests/TestNode.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | using System; 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | using System.Text; 19 | using Enyim.Caching.Memcached; 20 | using Enyim.Caching.Memcached.Protocol; 21 | using System.Net; 22 | using Amazon.ElastiCacheCluster.Operations; 23 | using Enyim.Caching.Memcached.Results; 24 | 25 | namespace LocalSimulationTests 26 | { 27 | public class TestNode : IMemcachedNode 28 | { 29 | private IPEndPoint end; 30 | public IPEndPoint EndPoint { get { return end; } set { this.end = value; } } 31 | 32 | private int requestNum = 1; 33 | 34 | public Enyim.Caching.Memcached.Results.IOperationResult Execute(IOperation op) 35 | { 36 | IConfigOperation getOp = op as IConfigOperation; 37 | 38 | byte[] bytes; 39 | 40 | switch (requestNum) 41 | { 42 | case 1: 43 | bytes = Encoding.UTF8.GetBytes(String.Format("{0}\r\ncluster.0001.use1.cache.amazon.aws.com|10.10.10.1|11211 cluster.0002.use1.cache.amazon.aws.com|10.10.10.2|11211 cluster.0003.use1.cache.amazon.aws.com|10.10.10.3|11211\r\n", this.requestNum)); 44 | break; 45 | case 2: 46 | bytes = Encoding.UTF8.GetBytes(String.Format("{0}\r\ncluster.0002.use1.cache.amazon.aws.com|10.10.10.2|11211 cluster.0003.use1.cache.amazon.aws.com|10.10.10.3|11211\r\n", this.requestNum)); 47 | break; 48 | default: 49 | bytes = Encoding.UTF8.GetBytes(String.Format("{0}\r\ncluster.0001.use1.cache.amazon.aws.com|10.10.10.1|11211\r\n", this.requestNum)); 50 | break; 51 | } 52 | this.requestNum++; 53 | 54 | var arr = new ArraySegment(bytes); 55 | getOp.ConfigResult = new CacheItem(0, arr); 56 | 57 | var result = new PooledSocketResult(); 58 | result.Success = true; 59 | return result; 60 | } 61 | 62 | public override string ToString() 63 | { 64 | return "TestingAWSInternal"; 65 | } 66 | 67 | public bool ExecuteAsync(IOperation op, Action next) 68 | { 69 | throw new NotImplementedException(); 70 | } 71 | 72 | public event Action Failed; 73 | 74 | public bool IsAlive 75 | { 76 | get { throw new NotImplementedException(); } 77 | } 78 | 79 | public bool Ping() 80 | { 81 | throw new NotImplementedException(); 82 | } 83 | 84 | public void Dispose() 85 | { 86 | throw new NotImplementedException(); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /LocalSimulationTests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/elasticache-cluster-config-net/30996c7f37dbb978f7d25f926847852a1d6ea50f/NOTICE.txt -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Amazon ElastiCache Cluster Configuration for .NET 2 | 3 | Amazon ElastiCache Cluster Configuration is an enhanced .NET library that supports connecting to an Amazon ElastiCache cluster for Auto Discovery. This client library is an extension built upon Enyim and is released under the [Apache 2.0 License](http://aws.amazon.com/apache2.0/). 4 | 5 | ## Usage 6 | 7 | To use Amazon ElastiCache with Auto Discovery, the ElastiCacheClusterConfig object must be passed to the MemcachedClient object. An ElastiCacheClusterConfig can be created from the App.config if there is a clusterclient section or you can pass a different section. 8 | 9 | var client = new MemcachedClient(new ElastiCacheClusterConfig()); 10 | 11 | ElastiCacheClusterConfig can also be instantiated by providing it an AutoConfigSetup or simply provide the hostname and port like so: 12 | 13 | var client = new MemcachedClient(new ElastiCacheClusterConfig("hostname", 11211)); 14 | 15 | Once instantiation occurs in this way, MemcachedClient can be used the same way as the [Enyim Client](https://github.com/enyim/EnyimMemcached). The only difference is a background process polls the cluster and updates nodes when changes occur. 16 | 17 | ## App.config 18 | The ElastiCacheClusterConfig object can be configured from an application's App.config file. Here is a sample App.config that configures the Amazon ElastiCache configuration URL and the polling interval at which the client will check for changes to the cluster. 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | The rest of the app.config configuration items for Enyim can be found [here](https://github.com/enyim/EnyimMemcached/wiki/MemcachedClient-Configuration#appconfig). 34 | 35 | ## Enyim Client 36 | Because this binary is used as a configuration object for the Enyim MemcachedClient, usage beyond instantiation is all exactly the same so refer to [this wiki](https://github.com/enyim/EnyimMemcached/wiki) or [this google group](https://groups.google.com/forum/#!forum/enyim-memcached) on how to use the actual client. 37 | 38 | ## Wiki 39 | The wiki found [here](https://github.com/awslabs/elasticache-cluster-config-net/wiki) goes into more detail on the usage of the ElastiCacheClusterConfig object as well as how the project takes advantage of Auto Discovery. 40 | 41 | ## Requirements 42 | 43 | You'll need .NET Framework 3.5 or later to use the precompiled binaries. To build the client, you'll need Visual Studio 2010 or higher. 44 | -------------------------------------------------------------------------------- /packages/repositories.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | --------------------------------------------------------------------------------