Skip to content

Commit 5ee5642

Browse files
committed
Market his: prune old order matching data. bitshares#454
This commit introduces two new options to market_history_plugin: * `max-order-his-records-per-market`: Will only store this amount of matched orders for each market in order history for querying, or those meet the other option, which has more data. (default: 1000) * `max-order-his-seconds-per-market`: Will only store matched orders in last X seconds for each market in order history for querying, or those meet the other option, which has more data. (default: 259200 (3 days))
1 parent af11a5b commit 5ee5642

File tree

2 files changed

+114
-49
lines changed

2 files changed

+114
-49
lines changed

libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp

+37-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828

2929
#include <fc/thread/future.hpp>
3030

31+
#include <boost/multi_index/composite_key.hpp>
32+
3133
namespace graphene { namespace market_history {
3234
using namespace chain;
3335

@@ -105,6 +107,21 @@ struct order_history_object : public abstract_object<order_history_object>
105107
fc::time_point_sec time;
106108
fill_order_operation op;
107109
};
110+
struct order_history_object_key_base_extractor
111+
{
112+
typedef asset_id_type result_type;
113+
result_type operator()(const order_history_object& o)const { return o.key.base; }
114+
};
115+
struct order_history_object_key_quote_extractor
116+
{
117+
typedef asset_id_type result_type;
118+
result_type operator()(const order_history_object& o)const { return o.key.quote; }
119+
};
120+
struct order_history_object_key_sequence_extractor
121+
{
122+
typedef int64_t result_type;
123+
result_type operator()(const order_history_object& o)const { return o.key.sequence; }
124+
};
108125

109126
struct by_key;
110127
typedef multi_index_container<
@@ -115,11 +132,28 @@ typedef multi_index_container<
115132
>
116133
> bucket_object_multi_index_type;
117134

135+
struct by_market_time;
118136
typedef multi_index_container<
119137
order_history_object,
120138
indexed_by<
121139
hashed_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
122-
ordered_unique< tag<by_key>, member< order_history_object, history_key, &order_history_object::key > >
140+
ordered_unique< tag<by_key>, member< order_history_object, history_key, &order_history_object::key > >,
141+
ordered_unique<
142+
tag<by_market_time>,
143+
composite_key<
144+
order_history_object,
145+
order_history_object_key_base_extractor,
146+
order_history_object_key_quote_extractor,
147+
member<order_history_object, time_point_sec, &order_history_object::time>,
148+
order_history_object_key_sequence_extractor
149+
>,
150+
composite_key_compare<
151+
std::less< asset_id_type >,
152+
std::less< asset_id_type >,
153+
std::greater< time_point_sec >,
154+
std::less< int64_t >
155+
>
156+
>
123157
>
124158
> order_history_multi_index_type;
125159

@@ -154,6 +188,8 @@ class market_history_plugin : public graphene::app::plugin
154188

155189
uint32_t max_history()const;
156190
const flat_set<uint32_t>& tracked_buckets()const;
191+
uint32_t max_order_his_records_per_market()const;
192+
uint32_t max_order_his_seconds_per_market()const;
157193

158194
private:
159195
friend class detail::market_history_plugin_impl;

libraries/plugins/market_history/market_history_plugin.cpp

+77-48
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ class market_history_plugin_impl
6161
market_history_plugin& _self;
6262
flat_set<uint32_t> _tracked_buckets;
6363
uint32_t _maximum_history_per_bucket_size = 1000;
64+
uint32_t _max_order_his_records_per_market = 1000;
65+
uint32_t _max_order_his_seconds_per_market = 259200;
6466
};
6567

6668

@@ -81,13 +83,12 @@ struct operation_process_fill_order
8183
void operator()( const fill_order_operation& o )const
8284
{
8385
//ilog( "processing ${o}", ("o",o) );
84-
const auto& buckets = _plugin.tracked_buckets();
8586
auto& db = _plugin.database();
8687
const auto& bucket_idx = db.get_index_type<bucket_index>();
8788
const auto& history_idx = db.get_index_type<history_index>().indices().get<by_key>();
89+
const auto& his_time_idx = db.get_index_type<history_index>().indices().get<by_market_time>();
8890

89-
auto time = db.head_block_time();
90-
91+
// To save new filled order data
9192
history_key hkey;
9293
hkey.base = o.pays.asset_id;
9394
hkey.quote = o.receives.asset_id;
@@ -104,39 +105,54 @@ struct operation_process_fill_order
104105

105106
db.create<order_history_object>( [&]( order_history_object& ho ) {
106107
ho.key = hkey;
107-
ho.time = time;
108+
ho.time = _now;
108109
ho.op = o;
109110
});
110111

111-
/*
112-
hkey.sequence += 200;
112+
// To remove old filled order data
113+
const auto max_records = _plugin.max_order_his_records_per_market();
114+
hkey.sequence += max_records;
113115
itr = history_idx.lower_bound( hkey );
114-
while( itr != history_idx.end() )
116+
if( itr != history_idx.end() && itr->key.base == hkey.base && itr->key.quote == hkey.quote )
115117
{
116-
if( itr->key.base == hkey.base && itr->key.quote == hkey.quote )
118+
const auto max_seconds = _plugin.max_order_his_seconds_per_market();
119+
fc::time_point_sec min_time;
120+
if( min_time + max_seconds < _now )
121+
min_time = _now - max_seconds;
122+
auto time_itr = his_time_idx.lower_bound( std::make_tuple( hkey.base, hkey.quote, min_time ) );
123+
if( time_itr != his_time_idx.end() && time_itr->key.base == hkey.base && time_itr->key.quote == hkey.quote )
117124
{
118-
db.remove( *itr );
119-
itr = history_idx.lower_bound( hkey );
125+
if( itr->key.sequence >= time_itr->key.sequence )
126+
{
127+
while( itr != history_idx.end() && itr->key.base == hkey.base && itr->key.quote == hkey.quote )
128+
{
129+
auto old_itr = itr;
130+
++itr;
131+
db.remove( *old_itr );
132+
}
133+
}
134+
else
135+
{
136+
while( time_itr != his_time_idx.end() && time_itr->key.base == hkey.base && time_itr->key.quote == hkey.quote )
137+
{
138+
auto old_itr = time_itr;
139+
++time_itr;
140+
db.remove( *old_itr );
141+
}
142+
}
120143
}
121-
else break;
122144
}
123-
*/
124145

125-
/* Note: below is not true, because global settlement creates only one fill_order_op.
126-
* for every matched order there are two fill order operations created, one for
127-
* each side. We can filter the duplicates by only considering the fill operations where
128-
* the base > quote
129-
*/
130-
/*
131-
if( o.pays.asset_id > o.receives.asset_id )
132-
{
133-
//ilog( " skipping because base > quote" );
134-
return;
135-
}
136-
*/
146+
// To update buckets data, only update for maker orders
137147
if( !o.is_maker )
138148
return;
139149

150+
const auto max_history = _plugin.max_history();
151+
if( max_history == 0 ) return;
152+
153+
const auto& buckets = _plugin.tracked_buckets();
154+
if( buckets.size() == 0 ) return;
155+
140156
bucket_key key;
141157
key.base = o.pays.asset_id;
142158
key.quote = o.receives.asset_id;
@@ -153,20 +169,19 @@ struct operation_process_fill_order
153169
if( fill_price.base.asset_id > fill_price.quote.asset_id )
154170
fill_price = ~fill_price;
155171

156-
auto max_history = _plugin.max_history();
157172
for( auto bucket : buckets )
158173
{
159174
auto bucket_num = _now.sec_since_epoch() / bucket;
160-
auto cutoff = fc::time_point_sec();
175+
fc::time_point_sec cutoff;
161176
if( bucket_num > max_history )
162-
cutoff = cutoff + fc::seconds( bucket * ( bucket_num - max_history ) );
177+
cutoff = cutoff + ( bucket * ( bucket_num - max_history ) );
163178

164179
key.seconds = bucket;
165-
key.open = fc::time_point_sec() + fc::seconds( bucket_num * bucket );
180+
key.open = fc::time_point_sec() + ( bucket_num * bucket );
166181

167182
const auto& by_key_idx = bucket_idx.indices().get<by_key>();
168-
auto itr = by_key_idx.find( key );
169-
if( itr == by_key_idx.end() )
183+
auto bucket_itr = by_key_idx.find( key );
184+
if( bucket_itr == by_key_idx.end() )
170185
{ // create new bucket
171186
/* const auto& obj = */
172187
db.create<bucket_object>( [&]( bucket_object& b ){
@@ -186,8 +201,8 @@ struct operation_process_fill_order
186201
}
187202
else
188203
{ // update existing bucket
189-
//wlog( " before updating bucket ${b}", ("b",*itr) );
190-
db.modify( *itr, [&]( bucket_object& b ){
204+
//wlog( " before updating bucket ${b}", ("b",*bucket_itr) );
205+
db.modify( *bucket_itr, [&]( bucket_object& b ){
191206
try {
192207
b.base_volume += trade_price.base.amount;
193208
} catch( fc::overflow_exception ) {
@@ -211,24 +226,23 @@ struct operation_process_fill_order
211226
b.low_quote = b.close_quote;
212227
}
213228
});
214-
//wlog( " after bucket bucket ${b}", ("b",*itr) );
229+
//wlog( " after bucket bucket ${b}", ("b",*bucket_itr) );
215230
}
216231

217-
if( max_history != 0 )
218232
{
219233
key.open = fc::time_point_sec();
220-
auto itr = by_key_idx.lower_bound( key );
234+
bucket_itr = by_key_idx.lower_bound( key );
221235

222-
while( itr != by_key_idx.end() &&
223-
itr->key.base == key.base &&
224-
itr->key.quote == key.quote &&
225-
itr->key.seconds == bucket &&
226-
itr->key.open < cutoff )
236+
while( bucket_itr != by_key_idx.end() &&
237+
bucket_itr->key.base == key.base &&
238+
bucket_itr->key.quote == key.quote &&
239+
bucket_itr->key.seconds == bucket &&
240+
bucket_itr->key.open < cutoff )
227241
{
228-
// elog( " removing old bucket ${b}", ("b", *itr) );
229-
auto old_itr = itr;
230-
++itr;
231-
db.remove( *old_itr );
242+
// elog( " removing old bucket ${b}", ("b", *bucket_itr) );
243+
auto old_bucket_itr = bucket_itr;
244+
++bucket_itr;
245+
db.remove( *old_bucket_itr );
232246
}
233247
}
234248
}
@@ -240,9 +254,6 @@ market_history_plugin_impl::~market_history_plugin_impl()
240254

241255
void market_history_plugin_impl::update_market_histories( const signed_block& b )
242256
{
243-
if( _maximum_history_per_bucket_size == 0 ) return;
244-
if( _tracked_buckets.size() == 0 ) return;
245-
246257
graphene::chain::database& db = database();
247258
const vector<optional< operation_history_object > >& hist = db.get_applied_operations();
248259
for( const optional< operation_history_object >& o_op : hist )
@@ -286,8 +297,12 @@ void market_history_plugin::plugin_set_program_options(
286297
cli.add_options()
287298
("bucket-size", boost::program_options::value<string>()->default_value("[60,300,900,1800,3600,14400,86400]"),
288299
"Track market history by grouping orders into buckets of equal size measured in seconds specified as a JSON array of numbers")
289-
("history-per-size", boost::program_options::value<uint32_t>()->default_value(1000),
300+
("history-per-size", boost::program_options::value<uint32_t>()->default_value(1000),
290301
"How far back in time to track history for each bucket size, measured in the number of buckets (default: 1000)")
302+
("max-order-his-records-per-market", boost::program_options::value<uint32_t>()->default_value(1000),
303+
"Will only store this amount of matched orders for each market in order history for querying, or those meet the other option, which has more data (default: 1000)")
304+
("max-order-his-seconds-per-market", boost::program_options::value<uint32_t>()->default_value(259200),
305+
"Will only store matched orders in last X seconds for each market in order history for querying, or those meet the other option, which has more data (default: 259200 (3 days))")
291306
;
292307
cfg.add(cli);
293308
}
@@ -306,6 +321,10 @@ void market_history_plugin::plugin_initialize(const boost::program_options::vari
306321
}
307322
if( options.count( "history-per-size" ) )
308323
my->_maximum_history_per_bucket_size = options["history-per-size"].as<uint32_t>();
324+
if( options.count( "max-order-his-records-per-market" ) )
325+
my->_max_order_his_records_per_market = options["max-order-his-records-per-market"].as<uint32_t>();
326+
if( options.count( "max-order-his-seconds-per-market" ) )
327+
my->_max_order_his_seconds_per_market = options["max-order-his-seconds-per-market"].as<uint32_t>();
309328
} FC_CAPTURE_AND_RETHROW() }
310329

311330
void market_history_plugin::plugin_startup()
@@ -322,4 +341,14 @@ uint32_t market_history_plugin::max_history()const
322341
return my->_maximum_history_per_bucket_size;
323342
}
324343

344+
uint32_t market_history_plugin::max_order_his_records_per_market()const
345+
{
346+
return my->_max_order_his_records_per_market;
347+
}
348+
349+
uint32_t market_history_plugin::max_order_his_seconds_per_market()const
350+
{
351+
return my->_max_order_his_seconds_per_market;
352+
}
353+
325354
} }

0 commit comments

Comments
 (0)