/*********************************************************************** * This file is part of iDempiere ERP Open Source * * http://www.idempiere.org * * * * Copyright (C) Contributors * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * * MA 02110-1301, USA. * **********************************************************************/ package com.trekglobal.idempiere.rest.api.model; import java.sql.ResultSet; import java.sql.Timestamp; import java.util.List; import java.util.Properties; import java.util.concurrent.ThreadLocalRandom; import org.compiere.model.MSysConfig; import org.compiere.model.Query; /** * Business model for REST_Webhook_Out_Log. * Manages webhook delivery lifecycle: pending, success, failed, abandoned. * Implements exponential backoff with jitter for retry scheduling. * * @author muriloht Murilo H. Torquato <murilo@muriloht.com> */ public class MRestWebhookOutLog extends X_REST_Webhook_Out_Log { private static final long serialVersionUID = 20260411L; /** * Window after which an IN_PROGRESS row is considered stuck (worker died * mid-flight) and may be reclaimed. Must be longer than the worst-case * HTTP timeout. 5 minutes covers the default 15s timeout with comfortable * margin and avoids reclaiming healthy long-running calls. */ public static final long STALE_INPROGRESS_MS = 5L * 60L * 1000L; /** * Backoff delays in seconds, indexed by attempt number. * Attempt 0 = immediate (first delivery), then exponential growth up to 24h. * {0s, 5s, 5m, 30m, 2h, 5h, 10h, 14h, 20h, 24h} */ public static final int[] BACKOFF_DELAYS = {0, 5, 300, 1800, 7200, 18000, 36000, 50400, 72000, 86400}; /** SysConfig: max stored length (chars) for ResponseBody and ErrorMessage. */ public static final String REST_WEBHOOK_LOG_MAX_LENGTH = "REST_WEBHOOK_LOG_MAX_LENGTH"; private static final int DEFAULT_LOG_MAX_LENGTH = 2000; public MRestWebhookOutLog(Properties ctx, int REST_Webhook_Out_Log_ID, String trxName) { super(ctx, REST_Webhook_Out_Log_ID, trxName); } public MRestWebhookOutLog(Properties ctx, String REST_Webhook_Out_Log_UU, String trxName) { super(ctx, REST_Webhook_Out_Log_UU, trxName); } public MRestWebhookOutLog(Properties ctx, ResultSet rs, String trxName) { super(ctx, rs, trxName); } /** * Get deliveries that need processing: due Pending rows plus stuck * IN_PROGRESS rows whose worker likely died mid-flight (Updated older * than {@link #STALE_INPROGRESS_MS}). * * @param ctx context * @param maxAttempts maximum number of attempts before abandoning * @param trxName transaction name * @return list of deliveries due for dispatch or recovery */ public static List getPendingRetries(Properties ctx, int maxAttempts, String trxName) { Timestamp staleThreshold = new Timestamp(System.currentTimeMillis() - STALE_INPROGRESS_MS); return new Query(ctx, Table_Name, "(" + " (DeliveryStatus=? AND (NextRetryAt IS NULL OR NextRetryAt<=getDate()))" + " OR (DeliveryStatus=? AND Updated<=?)" + ") AND Attempts