/** * Copyright 2015, Yahoo Inc. * Licensed under the terms of the Apache License 2.0. Please see LICENSE file in the project root for terms. */ package benchmark.common.advertising; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.Jedis; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.UUID; public class CampaignProcessorCommon { private static final Logger LOG = LoggerFactory.getLogger(CampaignProcessorCommon.class); private Jedis jedis; private Jedis flush_jedis; private Long lastWindowMillis; // Bucket -> Campaign_id -> Window private LRUHashMap<Long, HashMap<String, Window>> campaign_windows; private Set<CampaignWindowPair> need_flush; private long processed = 0; private static final Long time_divisor = 10000L; // 10 second windows public CampaignProcessorCommon(String redisServerHostname) { jedis = new Jedis(redisServerHostname); flush_jedis = new Jedis(redisServerHostname); } public void prepare() { campaign_windows = new LRUHashMap<Long, HashMap<String, Window>>(10); lastWindowMillis = System.currentTimeMillis(); need_flush = new HashSet<CampaignWindowPair>(); Runnable flusher = new Runnable() { public void run() { try { while (true) { Thread.sleep(1000); flushWindows(); lastWindowMillis = System.currentTimeMillis(); } } catch (InterruptedException e) { LOG.error("Interrupted", e); } } }; new Thread(flusher).start(); } public void execute(String campaign_id, String event_time) { Long timeBucket = Long.parseLong(event_time) / time_divisor; Window window = getWindow(timeBucket, campaign_id); window.seenCount++; CampaignWindowPair newPair = new CampaignWindowPair(campaign_id, window); synchronized(need_flush) { need_flush.add(newPair); } processed++; } private void writeWindow(String campaign, Window win) { String windowUUID = flush_jedis.hmget(campaign, win.timestamp).get(0); if (windowUUID == null) { windowUUID = UUID.randomUUID().toString(); flush_jedis.hset(campaign, win.timestamp, windowUUID); String windowListUUID = flush_jedis.hmget(campaign, "windows").get(0); if (windowListUUID == null) { windowListUUID = UUID.randomUUID().toString(); flush_jedis.hset(campaign, "windows", windowListUUID); } flush_jedis.lpush(windowListUUID, win.timestamp); } synchronized (campaign_windows) { flush_jedis.hincrBy(windowUUID, "seen_count", win.seenCount); win.seenCount = 0L; } flush_jedis.hset(windowUUID, "time_updated", Long.toString(System.currentTimeMillis())); flush_jedis.lpush("time_updated", Long.toString(System.currentTimeMillis())); } public void flushWindows() { synchronized (need_flush) { for (CampaignWindowPair pair : need_flush) { writeWindow(pair.campaign, pair.window); } need_flush.clear(); } } public static Window redisGetWindow(Long timeBucket, Long time_divisor) { Window win = new Window(); win.timestamp = Long.toString(timeBucket * time_divisor); win.seenCount = 0L; return win; } // Needs to be rewritten now that redisGetWindow has been simplified. // This can be greatly simplified. public Window getWindow(Long timeBucket, String campaign_id) { synchronized (campaign_windows) { HashMap<String, Window> bucket_map = campaign_windows.get(timeBucket); if (bucket_map == null) { // Try to pull from redis into cache. Window redisWindow = redisGetWindow(timeBucket, time_divisor); if (redisWindow != null) { bucket_map = new HashMap<String, Window>(); campaign_windows.put(timeBucket, bucket_map); bucket_map.put(campaign_id, redisWindow); return redisWindow; } // Otherwise, if nothing in redis: bucket_map = new HashMap<String, Window>(); campaign_windows.put(timeBucket, bucket_map); } // Bucket exists. Check the window. Window window = bucket_map.get(campaign_id); if (window == null) { // Try to pull from redis into cache. Window redisWindow = redisGetWindow(timeBucket, time_divisor); if (redisWindow != null) { bucket_map.put(campaign_id, redisWindow); return redisWindow; } // Otherwise, if nothing in redis: window = new Window(); window.timestamp = Long.toString(timeBucket * time_divisor); window.seenCount = 0L; bucket_map.put(campaign_id, redisWindow); } return window; } } }