docs: @Nonnull
      public static MemcachedNodesManager createFor(final String memcachedNodes, final String failoverNodes, final StorageKeyFormat storageKeyFormat,
              final StorageClientCallback storageClientCallback) {
  		if ( memcachedNodes == null || memcachedNodes.trim().isEmpty() ) {
  			throw new IllegalArgumentException("null or empty memcachedNodes not allowed.");
  		}
  		
          // Support a Redis URL in the form "redis://hostname:port" or "rediss://" (for SSL connections) like the client "Lettuce" does
  		if (memcachedNodes.startsWith("redis://") || memcachedNodes.startsWith("rediss://")) {
  		    // Redis configuration
  		    return new MemcachedNodesManager(memcachedNodes, new NodeIdList(), new ArrayList<String>(),
  		            new LinkedHashMap<InetSocketAddress, String>(), storageKeyFormat, storageClientCallback);
  		}
  
          if ( !NODES_PATTERN.matcher( memcachedNodes ).matches() && !SINGLE_NODE_PATTERN.matcher(memcachedNodes).matches()
          		&& !COUCHBASE_BUCKET_NODES_PATTERN.matcher(memcachedNodes).matches()) {
              throw new IllegalArgumentException( "Configured memcachedNodes attribute has wrong format, must match " + NODES_REGEX );
          }
  
          final Matcher singleNodeMatcher = SINGLE_NODE_PATTERN.matcher(memcachedNodes);
  
          // we have a linked hashmap to have insertion order for addresses
          final LinkedHashMap<InetSocketAddress, String> address2Ids = new LinkedHashMap<InetSocketAddress, String>(1);
  
          /**
           * If mutliple nodes are configured
           */
          if (singleNodeMatcher.matches()) {    // for single
              address2Ids.put(getSingleShortNodeDefinition(singleNodeMatcher), null);
          }
          else if (COUCHBASE_BUCKET_NODES_PATTERN.matcher(memcachedNodes).matches()) {    // for couchbase
              final Matcher matcher = COUCHBASE_BUCKET_NODE_PATTERN.matcher(memcachedNodes);
              while (matcher.find()) {
                  final String hostname = matcher.group( 1 );
                  final int port = Integer.parseInt( matcher.group( 2 ) );
                  address2Ids.put(new InetSocketAddress( hostname, port ), null);
              }
              if (address2Ids.isEmpty()) {
                  throw new IllegalArgumentException("All nodes are also configured as failover nodes,"
                          + " this is a configuration failure. In this case, you probably want to leave out the failoverNodes.");
              }
          }
          else { // If mutliple nodes are configured
              final Matcher matcher = NODE_PATTERN.matcher( memcachedNodes);
              while (matcher.find()) {
                  final Pair<String, InetSocketAddress> nodeInfo = getRegularNodeDefinition(matcher);
                  address2Ids.put(nodeInfo.getSecond(), nodeInfo.getFirst());
              }
              if (address2Ids.isEmpty()) {
                  throw new IllegalArgumentException("All nodes are also configured as failover nodes,"
                          + " this is a configuration failure. In this case, you probably want to leave out the failoverNodes.");
              }
          }
  
          final List<String> failoverNodeIds = initFailoverNodes(failoverNodes, address2Ids.values());
  
          // validate that for a single node there's no failover node specified as this does not make sense.
          if(address2Ids.size() == 1 && failoverNodeIds.size() >= 1) {
          	throw new IllegalArgumentException("For a single memcached node there should/must no failoverNodes be specified.");
          }
  
          final NodeIdList primaryNodeIds = new NodeIdList();
          for(final Map.Entry<InetSocketAddress, String> address2Id : address2Ids.entrySet()) {
  	        final String nodeId = address2Id.getValue();
  			if (nodeId != null && !failoverNodeIds.contains(nodeId) ) {
  	        	primaryNodeIds.add(nodeId);
  	        }
          }
  
          return new MemcachedNodesManager(memcachedNodes, primaryNodeIds, failoverNodeIds, address2Ids, storageKeyFormat, storageClientCallback);
  	}