Skip to content

Let the DataPartitionTable be automatically cleanable#14737

Merged
OneSizeFitsQuorum merged 3 commits intomasterfrom
auto-clean-partition-table
Jan 23, 2025
Merged

Let the DataPartitionTable be automatically cleanable#14737
OneSizeFitsQuorum merged 3 commits intomasterfrom
auto-clean-partition-table

Conversation

@CRZbulabula
Copy link
Copy Markdown
Contributor

A data partition is not necessary to exist when all corresponding data are expired given the pre-configurated TTL. Hence, this PR added a thread to automatically clean these expired data partitions, making the cache size of the DataPartitionTable is always acceptable! Specifically, the main updates include:

  1. In TTLManager, to retrieve the maximum TTL of a specific database, we implement getDatabaseMaxTTL().
  2. In TimePartitionUtils, to locate the time partition slot of the current timestamp, we implement the getCurrentTimePartitionSlot().
  3. In PartitionManager, we setup a periodically asynchronous thread, which combines the aforementioned interfaces to construct an "AutoCleanPartitionTablePlan." As a result, for each database, any data partition whose data are all expired will be deleted automatically by this independent thread.

Incidentally, the corresponding IT is available at "IoTDBPartitionTableAutoCleanTest."

@codecov
Copy link
Copy Markdown

codecov bot commented Jan 20, 2025

Codecov Report

Attention: Patch coverage is 41.02564% with 69 lines in your changes missing coverage. Please review.

Project coverage is 39.21%. Comparing base (bc5fdae) to head (8711a10).
Report is 25 commits behind head on master.

Files with missing lines Patch % Lines
...onfignode/procedure/PartitionTableAutoCleaner.java 20.00% 20 Missing ⚠️
.../org/apache/iotdb/commons/schema/ttl/TTLCache.java 0.00% 14 Missing ⚠️
...onfignode/persistence/partition/PartitionInfo.java 0.00% 8 Missing ⚠️
...t/write/partition/AutoCleanPartitionTablePlan.java 84.37% 5 Missing ⚠️
...apache/iotdb/commons/utils/TimePartitionUtils.java 16.66% 5 Missing ⚠️
.../iotdb/commons/partition/SeriesPartitionTable.java 20.00% 4 Missing ⚠️
...g/apache/iotdb/confignode/persistence/TTLInfo.java 0.00% 3 Missing ⚠️
...he/iotdb/commons/partition/DataPartitionTable.java 0.00% 3 Missing ⚠️
...che/iotdb/confignode/manager/ProcedureManager.java 33.33% 2 Missing ⚠️
.../persistence/partition/DatabasePartitionTable.java 0.00% 2 Missing ⚠️
... and 3 more
Additional details and impacted files
@@             Coverage Diff              @@
##             master   #14737      +/-   ##
============================================
+ Coverage     39.12%   39.21%   +0.09%     
  Complexity      193      193              
============================================
  Files          4417     4445      +28     
  Lines        281267   282533    +1266     
  Branches      34783    34861      +78     
============================================
+ Hits         110044   110800     +756     
- Misses       171223   171733     +510     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Copy Markdown
Collaborator

@liyuheng55555 liyuheng55555 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't finished reading all the code yet, I have some thoughts for discussing:

If the default execution interval is every 2 hours, then maybe adding a periodically executed Procedure is more lightweight compared to adding a new thread pool?

Comment on lines +130 to +156
for (int retry = 0; retry < 120; retry++) {
boolean partitionTableAutoCleaned = true;
TDataPartitionTableResp resp = client.getDataPartitionTable(req);
if (TSStatusCode.SUCCESS_STATUS.getStatusCode() == resp.getStatus().getCode()) {
Map<String, Map<TSeriesPartitionSlot, Map<TTimePartitionSlot, List<TConsensusGroupId>>>>
dataPartitionTable = resp.getDataPartitionTable();
for (Map.Entry<
String,
Map<TSeriesPartitionSlot, Map<TTimePartitionSlot, List<TConsensusGroupId>>>>
e1 : dataPartitionTable.entrySet()) {
for (Map.Entry<TSeriesPartitionSlot, Map<TTimePartitionSlot, List<TConsensusGroupId>>>
e2 : e1.getValue().entrySet()) {
if (e2.getValue().size() != 1) {
// The PartitionTable of each database should only contain 1 time partition slot
partitionTableAutoCleaned = false;
break;
}
}
if (!partitionTableAutoCleaned) {
break;
}
}
}
if (partitionTableAutoCleaned) {
return;
}
TimeUnit.SECONDS.sleep(1);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Considering using Awaitility to replace the outer for-loop and sleep
  2. (As Chatgpt suggested :) please checkout whether these two inner loop can be simplified, such as:
partitionTableAutoCleaned = resp.getDataPartitionTable().entrySet().stream()
   .flatMap(e1 -> e1.getValue().entrySet().stream())
   .allMatch(e2 -> e2.getValue().size() == 1);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An awesome suggestion! The corresponding test codes are simplified significantly!

for (String database : databases) {
long subTreeMaxTTL = getTTLManager().getDatabaseMaxTTL(database);
databaseTTLMap.put(
database, Math.max(subTreeMaxTTL, databaseTTLMap.getOrDefault(database, -1L)));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add some judgement like "isDatabaseExisted(database) && 0 < ttl && ttl < Long.MAX_VALUE" here to remove overhead?
BTW, If all the databases don't have ttl, we can logically just do this check and find that none of them need to be cleaned up, so there's no need to do a consensus write

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pinpointing this logic enhancement. The judgement is available at PartitionTableAutoCleaner.

this.regionMaintainer =
IoTDBThreadPoolFactory.newSingleThreadScheduledExecutor(
ThreadName.CONFIG_NODE_REGION_MAINTAINER.getName());
this.partitionCleaner =
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe try to reuse procedure periodic tasks

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I now employ the PartitionTableAutoCleaner rather than creating an extra thread pool.

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown
Collaborator

@liyuheng55555 liyuheng55555 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered concurrency issues, such as setTTL and Cleaner running simultaneously?

(I think one solution could be to clean up partitions only after they’ve exceeded the TTL by one hour, but you might have solved this in another way :)

@CRZbulabula
Copy link
Copy Markdown
Contributor Author

Have you considered concurrency issues, such as setTTL and Cleaner running simultaneously?

(I think one solution could be to clean up partitions only after they’ve exceeded the TTL by one hour, but you might have solved this in another way :)

Thanks for raising this critical issue. To address your concern, please refer to the autoCleanPartitionTable in the SeriesPartitionTable, as outlined below:

public void autoCleanPartitionTable(long TTL, TTimePartitionSlot currentTimeSlot) {
    seriesPartitionMap
        .entrySet()
        .removeIf(entry -> entry.getKey().getStartTime() + TTL < currentTimeSlot.getStartTime());
  }

Here, the removing condition is < rather than <=. As a result, a data partition is removed from cache only when it exceeded the TTL by a whole time partitioning interval. Therefore, the PartitionTable always contains a row of "empty" data partitions. I believe this approach can avoid this concurrency problem.

Copy link
Copy Markdown
Collaborator

@liyuheng55555 liyuheng55555 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work! Thank you for addressing my feedback patiently!

Copy link
Copy Markdown
Contributor

@OneSizeFitsQuorum OneSizeFitsQuorum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@OneSizeFitsQuorum OneSizeFitsQuorum merged commit 3369e7a into master Jan 23, 2025
@OneSizeFitsQuorum OneSizeFitsQuorum deleted the auto-clean-partition-table branch January 23, 2025 02:27
CRZbulabula added a commit that referenced this pull request Jan 23, 2025
* seems finished

* Use periodic procedure 4 partition table cleaner

* Update ThreadName.java
OneSizeFitsQuorum pushed a commit that referenced this pull request Jan 23, 2025
…14737) (#14759)

* Let the DataPartitionTable be automatically cleanable (#14737)

* seems finished

* Use periodic procedure 4 partition table cleaner

* Update ThreadName.java

* Update PartitionTableAutoCleaner.java
Caideyipi pushed a commit to Caideyipi/iotdb that referenced this pull request Mar 25, 2026
…pache#14737) (apache#14759)

* Let the DataPartitionTable be automatically cleanable (apache#14737)

* seems finished

* Use periodic procedure 4 partition table cleaner

* Update ThreadName.java

* Update PartitionTableAutoCleaner.java
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants