|
|
@@ -25,14 +25,21 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|
|
public class ConversionSyncService {
|
|
|
|
|
|
private static final Logger log = LoggerFactory.getLogger(ConversionSyncService.class);
|
|
|
- private static final int CONCURRENCY = 10;
|
|
|
+ private static final int REALTIME_CONCURRENCY = 10;
|
|
|
+ private static final int BACKFILL_CONCURRENCY = 16;
|
|
|
|
|
|
private final ConversionClient baiduClient;
|
|
|
private final RedisHotStore hotStore;
|
|
|
private final TencentClient tencentClient;
|
|
|
private final TiDBColdStore coldStore;
|
|
|
private final TagEventResolver tagEventResolver;
|
|
|
- private final ExecutorService executor;
|
|
|
+ private final ExecutorService realtimeExecutor;
|
|
|
+ private final ExecutorService backfillExecutor;
|
|
|
+
|
|
|
+ public enum SyncMode {
|
|
|
+ REALTIME,
|
|
|
+ BACKFILL
|
|
|
+ }
|
|
|
|
|
|
public ConversionSyncService(ConversionClient baiduClient, RedisHotStore hotStore,
|
|
|
TencentClient tencentClient, TiDBColdStore coldStore,
|
|
|
@@ -42,29 +49,41 @@ public class ConversionSyncService {
|
|
|
this.tencentClient = tencentClient;
|
|
|
this.coldStore = coldStore;
|
|
|
this.tagEventResolver = tagEventResolver;
|
|
|
- this.executor = Executors.newFixedThreadPool(CONCURRENCY,
|
|
|
+ this.realtimeExecutor = Executors.newFixedThreadPool(REALTIME_CONCURRENCY,
|
|
|
r -> { Thread t = new Thread(r, "conv-sync-worker"); t.setDaemon(true); return t; });
|
|
|
+ this.backfillExecutor = Executors.newFixedThreadPool(BACKFILL_CONCURRENCY,
|
|
|
+ r -> { Thread t = new Thread(r, "conv-backfill-worker"); t.setDaemon(true); return t; });
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 分页拉取百度转化数据并回传腾讯。
|
|
|
*/
|
|
|
public SyncResult syncTencentConversions(ConversionQuery query) throws Exception {
|
|
|
+ return syncTencentConversions(query, SyncMode.REALTIME);
|
|
|
+ }
|
|
|
+
|
|
|
+ public SyncResult syncTencentConversions(ConversionQuery query, SyncMode mode) throws Exception {
|
|
|
if (query.getPageSize() <= 0) query.setPageSize(1);
|
|
|
|
|
|
SyncResult result = new SyncResult();
|
|
|
int maxPages = 100;
|
|
|
for (int page = query.getPageSize(); ; page++) {
|
|
|
query.setPageSize(page);
|
|
|
- log.info("[ConversionSync] fetching page={}", page);
|
|
|
+ long pageStartNs = System.nanoTime();
|
|
|
+ log.info("[ConversionSync] fetching date={}, page={}, mode={}", query.getDate(), page, mode);
|
|
|
ConversionResponse response = baiduClient.queryPayments(query);
|
|
|
List<PaymentInfo> data = response.getData();
|
|
|
int dataSize = data != null ? data.size() : 0;
|
|
|
- log.info("[ConversionSync] page={} returned {} records, responsePageSize={}",
|
|
|
- page, dataSize, response.getPageSize());
|
|
|
+ log.info("[ConversionSync] date={}, page={} returned {} records, responsePageSize={}, mode={}",
|
|
|
+ query.getDate(), page, dataSize, response.getPageSize(), mode);
|
|
|
if (data != null) {
|
|
|
- processPayments(data, result);
|
|
|
+ processPayments(data, result, mode);
|
|
|
}
|
|
|
+ long pageCostMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - pageStartNs);
|
|
|
+ log.info("[ConversionSync] date={}, page={} processed, cost={}ms, totalFetched={}, matched={}, sent={}, failed={}, skipped={}, alreadySent={}, mode={}",
|
|
|
+ query.getDate(), page, pageCostMs,
|
|
|
+ result.fetched.get(), result.matched.get(), result.sent.get(), result.failed.get(),
|
|
|
+ result.skipped.get(), result.alreadySent.get(), mode);
|
|
|
if (response.getPageSize() <= page) break;
|
|
|
if (page >= maxPages) {
|
|
|
log.warn("[ConversionSync] reached max page limit ({}), stopping", maxPages);
|
|
|
@@ -74,7 +93,8 @@ public class ConversionSyncService {
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- private void processPayments(List<PaymentInfo> payments, SyncResult result) throws Exception {
|
|
|
+ private void processPayments(List<PaymentInfo> payments, SyncResult result, SyncMode mode) throws Exception {
|
|
|
+ ExecutorService executor = mode == SyncMode.BACKFILL ? backfillExecutor : realtimeExecutor;
|
|
|
List<CompletableFuture<Void>> futures = new ArrayList<>(payments.size());
|
|
|
for (PaymentInfo payment : payments) {
|
|
|
futures.add(CompletableFuture.runAsync(() -> processSinglePayment(payment, result), executor));
|
|
|
@@ -90,14 +110,18 @@ public class ConversionSyncService {
|
|
|
try {
|
|
|
bid = hotStore.findBidByQk(payment.getQk());
|
|
|
} catch (Exception e) {
|
|
|
- log.error("[ConversionSync] findBidByQk error: qk={}, {}", payment.getQk(), e.getMessage());
|
|
|
- result.failed.incrementAndGet();
|
|
|
- return;
|
|
|
+ log.warn("[ConversionSync] hot bid lookup failed, fallback to cold store: qk={}, {}", payment.getQk(), e.getMessage());
|
|
|
+ bid = coldStore != null ? coldStore.getBidByQk(payment.getQk()) : null;
|
|
|
}
|
|
|
|
|
|
if (bid == null) {
|
|
|
if (coldStore != null) {
|
|
|
- try { coldStore.saveConversion(conversionRecord(payment, null, "")); } catch (Exception ignored) {}
|
|
|
+ try {
|
|
|
+ coldStore.saveConversion(conversionRecord(payment, null, ""));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[ConversionSync] save conversion failed: qk={}, act={}, error={}",
|
|
|
+ payment.getQk(), payment.getAct(), e.getMessage(), e);
|
|
|
+ }
|
|
|
}
|
|
|
result.skipped.incrementAndGet();
|
|
|
return;
|
|
|
@@ -107,7 +131,12 @@ public class ConversionSyncService {
|
|
|
// bid 不是腾讯,或没有设备标识
|
|
|
if (!"tencent".equals(bid.getMedia())) {
|
|
|
if (coldStore != null) {
|
|
|
- try { coldStore.saveConversion(conversionRecord(payment, bid, bid.getMedia())); } catch (Exception ignored) {}
|
|
|
+ try {
|
|
|
+ coldStore.saveConversion(conversionRecord(payment, bid, bid.getMedia()));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[ConversionSync] save conversion failed: qk={}, act={}, error={}",
|
|
|
+ payment.getQk(), payment.getAct(), e.getMessage(), e);
|
|
|
+ }
|
|
|
}
|
|
|
result.skipped.incrementAndGet();
|
|
|
return;
|
|
|
@@ -117,7 +146,12 @@ public class ConversionSyncService {
|
|
|
log.info("[ConversionSync] 匹配到腾讯转化 | qk={} | act={} | deviceId={} | date={} | payment={}",
|
|
|
payment.getQk(), payment.getAct(), payment.getDeviceId(), payment.getDate(), payment.getPayment());
|
|
|
if (coldStore != null) {
|
|
|
- try { coldStore.saveConversion(conversionRecord(payment, bid, bid.getMedia())); } catch (Exception ignored) {}
|
|
|
+ try {
|
|
|
+ coldStore.saveConversion(conversionRecord(payment, bid, bid.getMedia()));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[ConversionSync] save conversion failed: qk={}, act={}, error={}",
|
|
|
+ payment.getQk(), payment.getAct(), e.getMessage(), e);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
String actionType = null;
|