After I didn't find any other solution, I ended up with a sequential workaround which will delete all records by time.
Disclaimer: Be sure to block all other actions that would rely on the index, because data will invalidate during deletion and new data maybe deleted as well. And you may add some authorization because not everybody should be able to delete all records.
I am using two indices from which I want to delete all records. The user has the possibility to define how much elements shall be deleted per index per transaction. I did not want to hardcode the limit. This gives the possibillity to heuristically find the maximum elements that can be deleted before a transaction time limit kicks in.
// in your .hpp
...
//@abi table testindex1 i64
struct teststruct {
uint64_t id;
account_name owner;
auto primary_key() const { return id; }
EOSLIB_SERIALIZE( limit_order, ( id )( owner ) )
};
typedef multi_index<N(testindex1), teststruct> testIndex1;
// same for testIndex2
...
//In your .cpp
// pLimit is the (max) number of elements
// which shall be deleted in each index
// before the next transaction is created
void MyContract::dtseq(uint64_t pLimit, uint128_t pLastId){
// block or it will run forever
if(pLimit > 0){
testIndex1 test1(_self, _self); // code, scope
uint64_t count = 0;
// iterate over first index
for(auto itr = test1.begin(); itr != test1.end() && count!=pLimit;) {
// delete element and update iterator reference
itr = test1.erase(itr);
count++;
}
// iterate over second index
testIndex2 test2(_self, _self); // code, scope
count = 0;
for(auto itr = test2.begin(); itr != test2.end() && count!=pLimit;) {
// delete element and update iterator reference
itr = test2.erase(itr);
count++;
}
// are elements left in one of the indices?
if(test1.begin() != test1.end() || test2.begin() != test2.end()){
// build new transactions which will call the same function
uint64_t time = current_time();
checksum256 calc_hash; // fc::sha256
uint128_t toHash, id2;
toHash = ((uint128_t(_self) << 64) | uint128_t(time));
// If not the first call use the pLastId to generate unique
if(pLastId != 0){
toHash = toHash | pLastId;
}
// Build a Hash so the transaction ids get distinct
sha256(reinterpret_cast<char *>(&toHash), 128, &calc_hash);
id2 = *reinterpret_cast<uint128_t *>(&calc_hash);
// First Transaction with unhashed id and passed id as input
transaction out;
out.actions.emplace_back(permission_level{_self, N(active)}, _self, N(dtseq), std::make_tuple(pLimit, toHash));
out.send(toHash, _self);
// Second Transction with hashed id and passed id as input
transaction out2;
out2.actions.emplace_back(permission_level{_self, N(active)}, _self, N(dtseq), std::make_tuple(pLimit, id2));
out2.send(id2, _self);
}
}
}
You can surely optimize by optimizing the loop logic or profit from parallel execution (when it is released). Also error messages and asserts should be included. Consider this as a demo and use it like you want to.
EDIT - Flooding/Recursive Improvement - High Speed Up and much more reliable
I discovered that sometimes the deferred transactions get cancelled and it takes much time to delete all records. Using this flooding approach by creating always 2 times more the tables will clear in very short time.
Call it with something like this. It will delete 7 entries in both indices in each call and create 14 deletions more in the next step. Then 28 and so on.
cleos push action mycontract dtseq '['7', '0']' -p mycontract@active