Skip to content

Commit 8f06c6c

Browse files
committed
fix(eu-data-act): drop ceiling clamp + add in-flight guard + dedicated rawJson state
P0: removed userMaxMs clamp on the adaptive poll delay - it would truncate the legitimate 15-min wait when config.interval is set lower, causing the adapter to hammer the portal every minute instead of locking onto the drop schedule. Floor stays at 30s; the portal cadence itself is the ceiling. P1: per-VIN inFlight flag prevents the manual refresh button from starting a second concurrent fetch while a previous one is still running (would have left an unreachable timer leaked). P1: rawJson dump no longer rides along the json2iob payload - large strings written every 15 min through json2iob become a history-adapter DB write amplifier. Now uses an explicit role=json state via extendObject/setState.
1 parent 651a774 commit 8f06c6c

2 files changed

Lines changed: 50 additions & 16 deletions

File tree

lib/euDataAct.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,6 @@ class EuDataActClient {
545545
const datasets = await this.listDatasets(vin, identifier);
546546
const contentDatasets = datasets.filter((d) => d && d.name && !d.name.endsWith(NO_CONTENT_SUFFIX));
547547
const newest = contentDatasets
548-
.slice()
549548
.sort((a, b) => String(b.createdOn || b.name).localeCompare(String(a.createdOn || a.name)))[0];
550549
this.log.debug(
551550
`[euDataAct] ${vin} list: ${datasets.length} datasets total, ${contentDatasets.length} with content`,

main.js

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6741,27 +6741,46 @@ class VwWeconnect extends utils.Adapter {
67416741
/**
67426742
* Adaptive poll for one VIN: fetch, then schedule the next call based on
67436743
* when the portal is expected to publish the next 15-min dataset.
6744+
*
6745+
* Uses a per-VIN in-flight flag so the manual refresh button (or rapid
6746+
* external scheduling calls) cannot start a second concurrent fetch — the
6747+
* second invocation is deferred until the running one finishes.
67446748
*/
67456749
_scheduleEuDataActPoll(vin, delayMs) {
67466750
if (!this.euDataActTimers) this.euDataActTimers = {};
6751+
if (!this.euDataActInFlight) this.euDataActInFlight = {};
67476752
if (this.euDataActTimers[vin]) {
67486753
clearTimeout(this.euDataActTimers[vin]);
67496754
}
67506755
this.euDataActTimers[vin] = setTimeout(async () => {
6751-
const next = await this.getEuDataActStatus(vin)
6752-
.catch((err) => {
6753-
this.log.error(`EU Data Act status for ${vin} failed: ${err.message || err}`);
6754-
return null;
6755-
});
6756-
// If the fetch didn't return a target time (NO_CONTENT, error,
6757-
// metadata still missing), fall back to a 1-min retry so we pick up
6758-
// the very next drop without waiting a full interval.
6756+
// Drop the timer handle now that the callback has been entered, so
6757+
// onUnload's cleanup loop doesn't try to clear an already-fired timer.
6758+
delete this.euDataActTimers[vin];
6759+
if (this.euDataActInFlight[vin]) {
6760+
// A previous fetch is still running (likely because the refresh
6761+
// button fired during a slow request). Re-arm in 5s and let the
6762+
// running call complete its own re-schedule.
6763+
this.log.debug(`EU Data Act: ${vin} fetch already in flight, deferring`);
6764+
this._scheduleEuDataActPoll(vin, 5000);
6765+
return;
6766+
}
6767+
this.euDataActInFlight[vin] = true;
6768+
let next = null;
6769+
try {
6770+
next = await this.getEuDataActStatus(vin);
6771+
} catch (err) {
6772+
this.log.error(`EU Data Act status for ${vin} failed: ${err.message || err}`);
6773+
} finally {
6774+
this.euDataActInFlight[vin] = false;
6775+
}
6776+
// 1-min fallback covers NO_CONTENT, hard error, missing metadata.
67596777
const fallbackMs = 60 * 1000;
6760-
const userMaxMs = Math.max(60, (this.config.interval || 15) * 60) * 1000;
67616778
let nextMs = typeof next === "number" && next > 0 ? next : fallbackMs;
6762-
// Never poll faster than 30s and never slower than the user-configured
6763-
// ceiling — the latter caps schedule drift if createdOn is way off.
6764-
nextMs = Math.max(30 * 1000, Math.min(nextMs, userMaxMs));
6779+
// Floor: never poll faster than 30s. No ceiling — the portal publishes
6780+
// exactly every 15 min, so any cap below that hammers the API for no
6781+
// benefit. config.interval is intentionally NOT used as a clamp on the
6782+
// success path; it would silently break the drop-locked schedule.
6783+
nextMs = Math.max(30 * 1000, nextMs);
67656784
this.log.debug(`EU Data Act: ${vin} next poll in ${Math.round(nextMs / 1000)}s`);
67666785
this._scheduleEuDataActPoll(vin, nextMs);
67676786
}, delayMs);
@@ -6864,13 +6883,28 @@ class VwWeconnect extends utils.Adapter {
68646883
if (result.datasetCreatedOn) {
68656884
payload._dataset_created_on = result.datasetCreatedOn;
68666885
}
6867-
if (this.config.rawJson) {
6868-
payload._raw_json = JSON.stringify(result.raw);
6869-
}
68706886
await this.json2iob.parse(vin + ".statuseudata", payload, {
68716887
forceIndex: true,
68726888
channelName: "EU Data Act 15-min dataset",
68736889
});
6890+
// The optional rawJson dump goes through extendObject/setState directly
6891+
// with role=json so it (a) gets the right role for the admin UI and
6892+
// (b) is easy to exclude from history adapters — at hundreds of KB
6893+
// every 15 min this is otherwise a serious DB write amplifier.
6894+
if (this.config.rawJson) {
6895+
await this.extendObjectAsync(vin + ".statuseudata.rawJson", {
6896+
type: "state",
6897+
common: {
6898+
name: "Raw EU Data Act dataset JSON",
6899+
role: "json",
6900+
type: "string",
6901+
read: true,
6902+
write: false,
6903+
},
6904+
native: {},
6905+
});
6906+
await this.setStateAsync(vin + ".statuseudata.rawJson", JSON.stringify(result.raw), true);
6907+
}
68746908
// Drop-locked rescheduling: the portal publishes one dataset per
68756909
// 15-min slot, timestamped in createdOn. Aim for createdOn + 15min +
68766910
// 45s buffer so we hit the new file as soon as it's provisioned. If
@@ -6924,6 +6958,7 @@ class VwWeconnect extends utils.Adapter {
69246958
for (const t of Object.values(this.euDataActTimers)) clearTimeout(t);
69256959
this.euDataActTimers = {};
69266960
}
6961+
this.euDataActInFlight = {};
69276962
this.mqttClient && this.mqttClient.end();
69286963
callback();
69296964
} catch (e) {

0 commit comments

Comments
 (0)