Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
37907 amit 1
package com.spice.profitmandi.dao.config;
2
 
3
import org.springframework.data.redis.cache.RedisCache;
4
import org.springframework.data.redis.cache.RedisCacheConfiguration;
5
import org.springframework.data.redis.cache.RedisCacheManager;
6
import org.springframework.data.redis.cache.RedisCacheWriter;
7
import org.springframework.data.redis.connection.RedisConnection;
8
import org.springframework.data.redis.connection.RedisConnectionFactory;
9
import org.springframework.data.redis.connection.ReturnType;
10
 
11
import java.nio.charset.StandardCharsets;
12
import java.time.Duration;
13
import java.util.Map;
14
import java.util.Set;
15
 
16
// An offer write that commits after a reader's transaction began leaves that reader with the old row;
17
// without this gate the reader caches it once the writer has evicted and it stays stale until the TTL
18
public class OfferCacheGate {
19
 
20
	private static final byte[] LAST_CHANGE_KEY = "offer.cache.lastChange".getBytes(StandardCharsets.UTF_8);
21
	// fofo, web and cron share one host clock; the margin absorbs small drift
22
	private static final long CLOCK_MARGIN_MS = 2000;
23
	// outside a transaction every statement reads fresh data, so only the loader's own run time matters
24
	private static final long NO_TX_LOOKBACK_MS = 10000;
25
 
26
	private static final byte[] MARK_SCRIPT = (
27
			"local cur = tonumber(redis.call('GET', KEYS[1]) or '0') "
28
					+ "if tonumber(ARGV[1]) > cur then redis.call('SET', KEYS[1], ARGV[1]) end "
29
					+ "return 1").getBytes(StandardCharsets.UTF_8);
30
 
31
	private static final byte[] PUT_IF_FRESH_SCRIPT = (
32
			"local last = tonumber(redis.call('GET', KEYS[1]) or '0') "
33
					+ "if tonumber(ARGV[1]) <= last + tonumber(ARGV[2]) then return 0 end "
34
					+ "if tonumber(ARGV[4]) > 0 then redis.call('SET', KEYS[2], ARGV[3], 'PX', ARGV[4]) "
35
					+ "else redis.call('SET', KEYS[2], ARGV[3]) end "
36
					+ "return 1").getBytes(StandardCharsets.UTF_8);
37
 
38
	private final RedisConnectionFactory connectionFactory;
39
 
40
	public OfferCacheGate(RedisConnectionFactory connectionFactory) {
41
		this.connectionFactory = connectionFactory;
42
	}
43
 
44
	// Called after an offer write's transaction completes, before its evictions
45
	public void markChanged() {
46
		eval(MARK_SCRIPT, 1, LAST_CHANGE_KEY, bytes(System.currentTimeMillis()));
47
	}
48
 
49
	private void putIfFresh(byte[] key, byte[] value, Duration ttl) {
50
		Long txStart = TxStartTrackingTransactionManager.currentTxStartMillis();
51
		long readerStart = txStart != null ? txStart : System.currentTimeMillis() - NO_TX_LOOKBACK_MS;
52
		eval(PUT_IF_FRESH_SCRIPT, 2, LAST_CHANGE_KEY, key, bytes(readerStart), bytes(CLOCK_MARGIN_MS), value,
53
				bytes(ttl.toMillis()));
54
	}
55
 
56
	public RedisCacheManager manager(RedisCacheConfiguration defaults, Map<String, RedisCacheConfiguration> initial,
57
			Set<String> gatedCaches) {
58
		return new Manager(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory), defaults, initial,
59
				gatedCaches);
60
	}
61
 
62
	private void eval(byte[] script, int numKeys, byte[]... keysAndArgs) {
63
		RedisConnection connection = connectionFactory.getConnection();
64
		try {
65
			connection.scriptingCommands().eval(script, ReturnType.INTEGER, numKeys, keysAndArgs);
66
		} finally {
67
			connection.close();
68
		}
69
	}
70
 
71
	private static byte[] bytes(long value) {
72
		return Long.toString(value).getBytes(StandardCharsets.UTF_8);
73
	}
74
 
75
	private class Manager extends RedisCacheManager {
76
		private final Set<String> gatedCaches;
77
 
78
		Manager(RedisCacheWriter writer, RedisCacheConfiguration defaults, Map<String, RedisCacheConfiguration> initial,
79
				Set<String> gatedCaches) {
80
			super(writer, defaults, initial);
81
			this.gatedCaches = gatedCaches;
82
		}
83
 
84
		@Override
85
		protected RedisCache createRedisCache(String name, RedisCacheConfiguration config) {
86
			RedisCache cache = super.createRedisCache(name, config);
87
			return gatedCaches.contains(name) ? new GatedCache(cache) : cache;
88
		}
89
	}
90
 
91
	private class GatedCache extends RedisCache {
92
		GatedCache(RedisCache cache) {
93
			super(cache.getName(), cache.getNativeCache(), cache.getCacheConfiguration());
94
		}
95
 
96
		@Override
97
		public void put(Object key, Object value) {
98
			Object cacheValue = preProcessCacheValue(value);
99
			if (cacheValue == null) {
100
				super.put(key, value);
101
				return;
102
			}
103
			putIfFresh(serializeCacheKey(createCacheKey(key)), serializeCacheValue(cacheValue),
104
					getCacheConfiguration().getTtl());
105
		}
106
	}
107
}