@Override public DynomiteClientDelegate build(String name, Map<String, Object> properties) { DynomiteDriverProperties props = convertSpringProperties(properties); return new DynomiteClientDelegate( name, new DynomiteClientFactory() .properties(props) .discoveryClient(discoveryClient) .build() ); }
@Bean DynomiteClientDelegateFactory dynomiteClientDelegateFactory(ObjectMapper objectMapper, Optional<DiscoveryClient> discoveryClient) { return new DynomiteClientDelegateFactory(objectMapper, discoveryClient); } }
public List<HostToken> getDynoHostTokens() { List<HostToken> tokens = new ArrayList<>(); List<Host> dynoHosts = getDynoHosts(); for (int i = 0; i < dynoHosts.size(); i++) { tokens.add(new HostToken(hosts.get(i).token, dynoHosts.get(i))); } return tokens; }
public DynoJedisClient build() { DynoJedisClient.Builder builder = new DynoJedisClient.Builder() .withApplicationName(properties.applicationName) .withDynomiteClusterName(properties.clusterName); if (properties.connectionPool == null || !"{}".equals(properties.connectionPool.getHashtag())) { // I don't really want to make the assumption all of our services will use hashtags, but they probably will... log.warn("Hashtag value has not been set. This will likely lead to inconsistent operations."); } Optional<DiscoveryClient> discovery = getDiscoveryClient(); if (discovery.isPresent()) { builder.withDiscoveryClient(discovery.get()); } else { if (properties.hosts.isEmpty()) { throw new BeanCreationException("Dynomite hosts must be set if discovery info not provided"); } properties.connectionPool .withTokenSupplier(new StaticTokenMapSupplier(properties.getDynoHostTokens())) .setLocalDataCenter(properties.localDatacenter) .setLocalRack(properties.localRack); builder .withHostSupplier(new StaticHostSupplier(properties.getDynoHosts())); } builder.withCPConfig(properties.connectionPool); return builder.build(); }
private void releaseRunKey(String agentType, long when) { final long newTtl = when - System.currentTimeMillis(); final boolean delete = newTtl < 2500L; redisClientDelegate.withCommandsClient(client -> { if (delete) { deleteLock(client, agentType); } else { ttlLock(client, agentType, newTtl); } }); }
@Override public void withPipeline(Consumer<RedisPipeline> f) { DynoJedisPipeline p = client.pipelined(); try { f.accept(p); } catch (DynoException |JedisException e) { try { p.close(); } catch (Exception ne) { throw new ClientDelegateException("Failed closing pipeline connection", ne); } throw new ClientDelegateException("Internal exception during pipelining", e); } }
/** * Spring config parsing is v. dumb. It will start making any iterable a map after a certain * depth, so this method massages the data into something we can actually use. */ @SuppressWarnings("unchecked") private DynomiteDriverProperties convertSpringProperties(Map<String, Object> properties) { Map<String, Object> props = new HashMap<>(properties); Map<String, Object> springHosts = (Map<String, Object>) properties.get("hosts"); if (springHosts != null) { props.put("hosts", new ArrayList<>(springHosts.values())); } ObjectMapper mapper = objectMapper.copy(); SimpleModule simpleModule = new SimpleModule(); simpleModule.addDeserializer( ConnectionPoolConfigurationImpl.class, new ConnectionPoolConfigurationImplDeserializer((String) props.get("applicationName")) ); mapper.registerModule(simpleModule); return mapper.convertValue(props, DynomiteDriverProperties.class); }
@SuppressWarnings("unchecked") @Override public ConnectionPoolConfigurationImpl deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, IllegalMappingMethodAccess { ConnectionPoolConfigurationImpl result = new ConnectionPoolConfigurationImpl(name); Map<String, Object> raw = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); raw.putAll(ctxt.readValue(p, Map.class)); Arrays.stream(ConnectionPoolConfigurationImpl.class.getDeclaredMethods()) .filter(it -> it.getName().startsWith("set")) .forEach(method -> { String fieldName = method.getName().substring(3); Object value = raw.get(fieldName); if (value != null) { try { method.setAccessible(true); method.invoke(result, value); } catch (IllegalAccessException|InvocationTargetException e) { throw new IllegalMappingMethodAccess(format("Could not invoke %s", method.getName()), e); } } }); return result; } }
private boolean acquireRunKey(String agentType, long timeout) { // This isn't as safe as the vanilla Redis impl because the call isn't atomic, but it's the best we can do until // dynomite adds support for `String set(String key, String value, String nxxx, String expx, long time)` (which // they are working on). String identity = nodeIdentity.getNodeIdentity(); return redisClientDelegate.withCommandsClient(client -> { return Failsafe .with(ACQUIRE_LOCK_RETRY_POLICY) .get(() -> { String response = client.get(agentType); if (response == null && client.setnx(agentType, identity) == 1) { client.pexpireAt(agentType, System.currentTimeMillis() + timeout); return true; } if (client.ttl(agentType) == -1) { log.warn("Detected potential deadlocked agent, removing lock key: " + agentType); client.del(agentType); } return false; }); }); }
@Override public <R> R withPipeline(Function<RedisPipeline, R> f) { DynoJedisPipeline p = client.pipelined(); try { return f.apply(p); } catch (DynoException|JedisException e) { try { p.close(); } catch (Exception ne) { throw new ClientDelegateException("Failed closing pipeline connection", ne); } throw new ClientDelegateException("Internal exception during pipelining", e); } }