record tenant_id && adapitve update resource limit when advance clock in throttle unit
This commit is contained in:
@ -51,7 +51,8 @@ void ObTenantMdsAllocator::init_throttle_config(int64_t &resource_limit, int64_t
|
|||||||
max_duration = MDS_THROTTLE_MAX_DURATION;
|
max_duration = MDS_THROTTLE_MAX_DURATION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void ObTenantMdsAllocator::adaptive_update_limit(const int64_t holding_size,
|
void ObTenantMdsAllocator::adaptive_update_limit(const int64_t tenant_id,
|
||||||
|
const int64_t holding_size,
|
||||||
const int64_t config_specify_resource_limit,
|
const int64_t config_specify_resource_limit,
|
||||||
int64_t &resource_limit,
|
int64_t &resource_limit,
|
||||||
int64_t &last_update_limit_ts,
|
int64_t &last_update_limit_ts,
|
||||||
|
|||||||
@ -234,7 +234,8 @@ void ObMemstoreAllocator::init_throttle_config(int64_t &resource_limit,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObMemstoreAllocator::adaptive_update_limit(const int64_t holding_size,
|
void ObMemstoreAllocator::adaptive_update_limit(const int64_t tenant_id,
|
||||||
|
const int64_t holding_size,
|
||||||
const int64_t config_specify_resource_limit,
|
const int64_t config_specify_resource_limit,
|
||||||
int64_t &resource_limit,
|
int64_t &resource_limit,
|
||||||
int64_t &last_update_limit_ts,
|
int64_t &last_update_limit_ts,
|
||||||
|
|||||||
@ -31,8 +31,12 @@ namespace share {
|
|||||||
|
|
||||||
void ObSharedMemAllocMgr::update_throttle_config()
|
void ObSharedMemAllocMgr::update_throttle_config()
|
||||||
{
|
{
|
||||||
int64_t tenant_id = MTL_ID();
|
if (MTL_ID() != tenant_id_) {
|
||||||
int64_t total_memory = lib::get_tenant_memory_limit(tenant_id);
|
SHARE_LOG_RET(ERROR, OB_ERR_UNEXPECTED, "update throttle config in an invalid tenant", K(MTL_ID()), K(tenant_id_));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t total_memory = lib::get_tenant_memory_limit(tenant_id_);
|
||||||
omt::ObTenantConfigGuard tenant_config(TENANT_CONF(MTL_ID()));
|
omt::ObTenantConfigGuard tenant_config(TENANT_CONF(MTL_ID()));
|
||||||
if (tenant_config.is_valid()) {
|
if (tenant_config.is_valid()) {
|
||||||
int64_t share_mem_limit_percentage = tenant_config->_tx_share_memory_limit_percentage;
|
int64_t share_mem_limit_percentage = tenant_config->_tx_share_memory_limit_percentage;
|
||||||
@ -46,10 +50,10 @@ void ObSharedMemAllocMgr::update_throttle_config()
|
|||||||
share_mem_limit_percentage = memstore_limit_percentage + 10;
|
share_mem_limit_percentage = memstore_limit_percentage + 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t share_mem_limit = total_memory * share_mem_limit_percentage / 100LL;
|
int64_t share_mem_limit = total_memory / 100 * share_mem_limit_percentage;
|
||||||
int64_t memstore_limit = total_memory * memstore_limit_percentage / 100LL;
|
int64_t memstore_limit = total_memory / 100 * memstore_limit_percentage;
|
||||||
int64_t tx_data_limit = total_memory * tx_data_limit_percentage / 100LL;
|
int64_t tx_data_limit = total_memory / 100 * tx_data_limit_percentage;
|
||||||
int64_t mds_limit = total_memory * mds_limit_percentage / 100LL;
|
int64_t mds_limit = total_memory / 100 * mds_limit_percentage;
|
||||||
|
|
||||||
bool share_config_changed = false;
|
bool share_config_changed = false;
|
||||||
(void)share_resource_throttle_tool_.update_throttle_config<FakeAllocatorForTxShare>(
|
(void)share_resource_throttle_tool_.update_throttle_config<FakeAllocatorForTxShare>(
|
||||||
@ -70,39 +74,22 @@ void ObSharedMemAllocMgr::update_throttle_config()
|
|||||||
if (share_config_changed || memstore_config_changed || tx_data_config_changed || mds_config_changed) {
|
if (share_config_changed || memstore_config_changed || tx_data_config_changed || mds_config_changed) {
|
||||||
SHARE_LOG(INFO,
|
SHARE_LOG(INFO,
|
||||||
"[Throttle] Update Config",
|
"[Throttle] Update Config",
|
||||||
|
K(tenant_id_),
|
||||||
|
K(total_memory),
|
||||||
K(share_mem_limit_percentage),
|
K(share_mem_limit_percentage),
|
||||||
|
K(share_mem_limit),
|
||||||
K(memstore_limit_percentage),
|
K(memstore_limit_percentage),
|
||||||
|
K(memstore_limit),
|
||||||
K(tx_data_limit_percentage),
|
K(tx_data_limit_percentage),
|
||||||
|
K(tx_data_limit),
|
||||||
K(mds_limit_percentage),
|
K(mds_limit_percentage),
|
||||||
|
K(mds_limit),
|
||||||
K(trigger_percentage),
|
K(trigger_percentage),
|
||||||
K(max_duration));
|
K(max_duration));
|
||||||
|
|
||||||
if (share_config_changed) {
|
|
||||||
SHARE_LOG(INFO,
|
|
||||||
"[Throttle] Update Config",
|
|
||||||
THROTTLE_CONFIG_LOG(FakeAllocatorForTxShare, share_mem_limit, trigger_percentage, max_duration));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (memstore_config_changed) {
|
|
||||||
SHARE_LOG(INFO,
|
|
||||||
"[Throttle] Update Config",
|
|
||||||
THROTTLE_CONFIG_LOG(ObMemstoreAllocator, memstore_limit, trigger_percentage, max_duration));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tx_data_config_changed) {
|
|
||||||
SHARE_LOG(INFO,
|
|
||||||
"[Throttle] Update Config",
|
|
||||||
THROTTLE_CONFIG_LOG(ObTenantTxDataAllocator, tx_data_limit, trigger_percentage, max_duration));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mds_config_changed) {
|
|
||||||
SHARE_LOG(INFO,
|
|
||||||
"[Throttle] Update Config",
|
|
||||||
THROTTLE_CONFIG_LOG(ObTenantMdsAllocator, mds_limit, trigger_percentage, max_duration));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
SHARE_LOG_RET(WARN, OB_INVALID_CONFIG, "invalid tenant config", K(tenant_id), K(total_memory));
|
SHARE_LOG_RET(WARN, OB_INVALID_CONFIG, "invalid tenant config", K(tenant_id_), K(total_memory));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,8 +23,7 @@ namespace share {
|
|||||||
|
|
||||||
class ObSharedMemAllocMgr {
|
class ObSharedMemAllocMgr {
|
||||||
public:
|
public:
|
||||||
ObSharedMemAllocMgr()
|
ObSharedMemAllocMgr(): share_resource_throttle_tool_(),
|
||||||
: share_resource_throttle_tool_(),
|
|
||||||
memstore_allocator_(),
|
memstore_allocator_(),
|
||||||
tx_data_allocator_(),
|
tx_data_allocator_(),
|
||||||
mds_allocator_() {}
|
mds_allocator_() {}
|
||||||
@ -47,8 +46,9 @@ public:
|
|||||||
share_resource_throttle_tool_.init(&memstore_allocator_, &tx_data_allocator_, &mds_allocator_))) {
|
share_resource_throttle_tool_.init(&memstore_allocator_, &tx_data_allocator_, &mds_allocator_))) {
|
||||||
SHARE_LOG(ERROR, "init share resource throttle tool failed", KR(ret));
|
SHARE_LOG(ERROR, "init share resource throttle tool failed", KR(ret));
|
||||||
} else {
|
} else {
|
||||||
|
tenant_id_ = MTL_ID();
|
||||||
share_resource_throttle_tool_.enable_adaptive_limit<FakeAllocatorForTxShare>();
|
share_resource_throttle_tool_.enable_adaptive_limit<FakeAllocatorForTxShare>();
|
||||||
SHARE_LOG(INFO, "finish init mtl share mem allocator mgr", K(MTL_ID()), KP(this));
|
SHARE_LOG(INFO, "finish init mtl share mem allocator mgr", K(tenant_id_), KP(this));
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -71,6 +71,7 @@ private:
|
|||||||
void update_mds_throttle_config_(const int64_t total_memory, omt::ObTenantConfigGuard &config);
|
void update_mds_throttle_config_(const int64_t total_memory, omt::ObTenantConfigGuard &config);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
int64_t tenant_id_;
|
||||||
TxShareThrottleTool share_resource_throttle_tool_;
|
TxShareThrottleTool share_resource_throttle_tool_;
|
||||||
ObMemstoreAllocator memstore_allocator_;
|
ObMemstoreAllocator memstore_allocator_;
|
||||||
ObTenantTxDataAllocator tx_data_allocator_;
|
ObTenantTxDataAllocator tx_data_allocator_;
|
||||||
|
|||||||
@ -48,7 +48,8 @@ void ObTenantTxDataAllocator::init_throttle_config(int64_t &resource_limit,
|
|||||||
max_duration = TX_DATA_THROTTLE_MAX_DURATION;
|
max_duration = TX_DATA_THROTTLE_MAX_DURATION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void ObTenantTxDataAllocator::adaptive_update_limit(const int64_t holding_size,
|
void ObTenantTxDataAllocator::adaptive_update_limit(const int64_t tenant_id,
|
||||||
|
const int64_t holding_size,
|
||||||
const int64_t config_specify_resource_limit,
|
const int64_t config_specify_resource_limit,
|
||||||
int64_t &resource_limit,
|
int64_t &resource_limit,
|
||||||
int64_t &last_update_limit_ts,
|
int64_t &last_update_limit_ts,
|
||||||
|
|||||||
@ -63,7 +63,8 @@ void FakeAllocatorForTxShare::init_throttle_config(int64_t &resource_limit,
|
|||||||
* @param[out] last_update_limit_ts last update ts (for performance optimization)
|
* @param[out] last_update_limit_ts last update ts (for performance optimization)
|
||||||
* @param[out] is_updated to decide if update_decay_factor() is needed
|
* @param[out] is_updated to decide if update_decay_factor() is needed
|
||||||
*/
|
*/
|
||||||
void FakeAllocatorForTxShare::adaptive_update_limit(const int64_t holding_size,
|
void FakeAllocatorForTxShare::adaptive_update_limit(const int64_t tenant_id,
|
||||||
|
const int64_t holding_size,
|
||||||
const int64_t config_specify_resource_limit,
|
const int64_t config_specify_resource_limit,
|
||||||
int64_t &resource_limit,
|
int64_t &resource_limit,
|
||||||
int64_t &last_update_limit_ts,
|
int64_t &last_update_limit_ts,
|
||||||
@ -76,7 +77,7 @@ void FakeAllocatorForTxShare::adaptive_update_limit(const int64_t holding_size,
|
|||||||
int64_t cur_ts = ObClockGenerator::getClock();
|
int64_t cur_ts = ObClockGenerator::getClock();
|
||||||
int64_t old_ts = last_update_limit_ts;
|
int64_t old_ts = last_update_limit_ts;
|
||||||
if ((cur_ts - old_ts > UPDATE_LIMIT_INTERVAL) && ATOMIC_BCAS(&last_update_limit_ts, old_ts, cur_ts)) {
|
if ((cur_ts - old_ts > UPDATE_LIMIT_INTERVAL) && ATOMIC_BCAS(&last_update_limit_ts, old_ts, cur_ts)) {
|
||||||
int64_t remain_memory = lib::get_tenant_memory_remain(MTL_ID());
|
int64_t remain_memory = lib::get_tenant_memory_remain(tenant_id);
|
||||||
int64_t usable_remain_memory = remain_memory / 100 * USABLE_REMAIN_MEMORY_PERCETAGE;
|
int64_t usable_remain_memory = remain_memory / 100 * USABLE_REMAIN_MEMORY_PERCETAGE;
|
||||||
if (remain_memory > MAX_UNUSABLE_MEMORY) {
|
if (remain_memory > MAX_UNUSABLE_MEMORY) {
|
||||||
usable_remain_memory = std::max(usable_remain_memory, remain_memory - MAX_UNUSABLE_MEMORY);
|
usable_remain_memory = std::max(usable_remain_memory, remain_memory - MAX_UNUSABLE_MEMORY);
|
||||||
@ -96,6 +97,7 @@ void FakeAllocatorForTxShare::adaptive_update_limit(const int64_t holding_size,
|
|||||||
if (is_updated && REACH_TIME_INTERVAL(10LL * 1000LL * 1000LL)) {
|
if (is_updated && REACH_TIME_INTERVAL(10LL * 1000LL * 1000LL)) {
|
||||||
SHARE_LOG(INFO,
|
SHARE_LOG(INFO,
|
||||||
"adaptive update",
|
"adaptive update",
|
||||||
|
"Tenant ID", tenant_id,
|
||||||
"Config Specify Resource Limit(MB)", config_specify_resource_limit / 1024 / 1024,
|
"Config Specify Resource Limit(MB)", config_specify_resource_limit / 1024 / 1024,
|
||||||
"TxShare Current Memory Limit(MB)", resource_limit / 1024 / 1024,
|
"TxShare Current Memory Limit(MB)", resource_limit / 1024 / 1024,
|
||||||
"Holding Memory(MB)", holding_size / 1024 / 1024,
|
"Holding Memory(MB)", holding_size / 1024 / 1024,
|
||||||
|
|||||||
@ -28,7 +28,8 @@
|
|||||||
} \
|
} \
|
||||||
static int64_t resource_unit_size(); \
|
static int64_t resource_unit_size(); \
|
||||||
static void init_throttle_config(int64_t &resource_limit, int64_t &trigger_percentage, int64_t &max_duration); \
|
static void init_throttle_config(int64_t &resource_limit, int64_t &trigger_percentage, int64_t &max_duration); \
|
||||||
static void adaptive_update_limit(const int64_t holding_size, \
|
static void adaptive_update_limit(const int64_t tenant_id, \
|
||||||
|
const int64_t holding_size, \
|
||||||
const int64_t config_specify_resource_limit, \
|
const int64_t config_specify_resource_limit, \
|
||||||
int64_t &resource_limit, \
|
int64_t &resource_limit, \
|
||||||
int64_t &last_update_limit_ts, \
|
int64_t &last_update_limit_ts, \
|
||||||
@ -38,6 +39,7 @@
|
|||||||
\
|
\
|
||||||
struct FakeAllocatorFor##ThrottleName { \
|
struct FakeAllocatorFor##ThrottleName { \
|
||||||
DEFINE_CUSTOM_FUNC_FOR_THROTTLE(ThrottleName); \
|
DEFINE_CUSTOM_FUNC_FOR_THROTTLE(ThrottleName); \
|
||||||
|
int64_t tenant_id_; \
|
||||||
}; \
|
}; \
|
||||||
\
|
\
|
||||||
LST_DEFINE(__VA_ARGS__); \
|
LST_DEFINE(__VA_ARGS__); \
|
||||||
|
|||||||
@ -184,6 +184,7 @@ private:
|
|||||||
int64_t last_advance_clock_ts_us_;
|
int64_t last_advance_clock_ts_us_;
|
||||||
int64_t last_print_throttle_info_ts_;
|
int64_t last_print_throttle_info_ts_;
|
||||||
int64_t last_update_limit_ts_;
|
int64_t last_update_limit_ts_;
|
||||||
|
int64_t tenant_id_;
|
||||||
double decay_factor_;
|
double decay_factor_;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -25,10 +25,11 @@ namespace oceanbase {
|
|||||||
namespace share {
|
namespace share {
|
||||||
|
|
||||||
#define THROTTLE_UNIT_INFO \
|
#define THROTTLE_UNIT_INFO \
|
||||||
"Unit Name", unit_name_, "Config Specify Resource Limit(MB)", config_specify_resource_limit_ / 1024 / 1024, \
|
KP(this), K(enable_adaptive_limit_), "Unit Name", unit_name_, "Config Specify Resource Limit(MB)", \
|
||||||
"Resource Limit(MB)", resource_limit_ / 1024 / 1024, "Throttle Trigger(MB)", \
|
config_specify_resource_limit_ / 1024 / 1024, "Resource Limit(MB)", resource_limit_ / 1024 / 1024, \
|
||||||
resource_limit_ *throttle_trigger_percentage_ / 100 / 1024 / 1024, "Throttle Percentage", \
|
"Throttle Trigger(MB)", resource_limit_ *throttle_trigger_percentage_ / 100 / 1024 / 1024, \
|
||||||
throttle_trigger_percentage_, "Max Duration(us)", throttle_max_duration_, "Decay Factor", decay_factor_
|
"Throttle Percentage", throttle_trigger_percentage_, "Max Duration(us)", throttle_max_duration_, "Decay Factor", \
|
||||||
|
decay_factor_
|
||||||
|
|
||||||
template <typename ALLOCATOR>
|
template <typename ALLOCATOR>
|
||||||
int ObThrottleUnit<ALLOCATOR>::init()
|
int ObThrottleUnit<ALLOCATOR>::init()
|
||||||
@ -48,9 +49,11 @@ int ObThrottleUnit<ALLOCATOR>::init()
|
|||||||
(void)update_decay_factor_();
|
(void)update_decay_factor_();
|
||||||
config_specify_resource_limit_ = resource_limit_;
|
config_specify_resource_limit_ = resource_limit_;
|
||||||
enable_adaptive_limit_ = false;
|
enable_adaptive_limit_ = false;
|
||||||
|
tenant_id_ = MTL_ID();
|
||||||
is_inited_ = true;
|
is_inited_ = true;
|
||||||
SHARE_LOG(INFO,
|
SHARE_LOG(INFO,
|
||||||
"[Throttle]Init throttle config finish",
|
"[Throttle]Init throttle config finish",
|
||||||
|
K(tenant_id_),
|
||||||
K(unit_name_),
|
K(unit_name_),
|
||||||
K(resource_limit_),
|
K(resource_limit_),
|
||||||
K(config_specify_resource_limit_),
|
K(config_specify_resource_limit_),
|
||||||
@ -73,11 +76,8 @@ int ObThrottleUnit<ALLOCATOR>::alloc_resource(const int64_t holding_size,
|
|||||||
// do adaptive update resource limit if needed
|
// do adaptive update resource limit if needed
|
||||||
if (enable_adaptive_limit_) {
|
if (enable_adaptive_limit_) {
|
||||||
bool is_updated = false;
|
bool is_updated = false;
|
||||||
ALLOCATOR::adaptive_update_limit(holding_size,
|
ALLOCATOR::adaptive_update_limit(
|
||||||
config_specify_resource_limit_,
|
tenant_id_, holding_size, config_specify_resource_limit_, resource_limit_, last_update_limit_ts_, is_updated);
|
||||||
resource_limit_,
|
|
||||||
last_update_limit_ts_,
|
|
||||||
is_updated);
|
|
||||||
if (is_updated) {
|
if (is_updated) {
|
||||||
(void)update_decay_factor_(true /* is_adaptive_update */);
|
(void)update_decay_factor_(true /* is_adaptive_update */);
|
||||||
}
|
}
|
||||||
@ -165,6 +165,7 @@ void ObThrottleUnit<ALLOCATOR>::print_throttle_info_(const int64_t holding_size,
|
|||||||
|
|
||||||
SHARE_LOG(INFO,
|
SHARE_LOG(INFO,
|
||||||
"[Throttling] (report write throttle info) Size Info",
|
"[Throttling] (report write throttle info) Size Info",
|
||||||
|
"tenant_id", tenant_id_,
|
||||||
"Throttle Unit Name", unit_name_,
|
"Throttle Unit Name", unit_name_,
|
||||||
"Allocating Resource Size", alloc_size,
|
"Allocating Resource Size", alloc_size,
|
||||||
"Holding Resource Size", holding_size,
|
"Holding Resource Size", holding_size,
|
||||||
@ -172,6 +173,7 @@ void ObThrottleUnit<ALLOCATOR>::print_throttle_info_(const int64_t holding_size,
|
|||||||
"Released Sequence", cur_clock,
|
"Released Sequence", cur_clock,
|
||||||
"Release Speed", release_speed,
|
"Release Speed", release_speed,
|
||||||
"Total Resource Limit", resource_limit_,
|
"Total Resource Limit", resource_limit_,
|
||||||
|
"Config Specify Limit", config_specify_resource_limit_,
|
||||||
"Throttle Trigger Threshold", throttle_trigger);
|
"Throttle Trigger Threshold", throttle_trigger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -271,6 +273,15 @@ void ObThrottleUnit<ALLOCATOR>::advance_clock(const int64_t holding_size)
|
|||||||
int64_t old_ts = last_advance_clock_ts_us_;
|
int64_t old_ts = last_advance_clock_ts_us_;
|
||||||
const int64_t advance_us = cur_ts - old_ts;
|
const int64_t advance_us = cur_ts - old_ts;
|
||||||
if ((advance_us > ADVANCE_CLOCK_INTERVAL) && ATOMIC_BCAS(&last_advance_clock_ts_us_, old_ts, cur_ts)) {
|
if ((advance_us > ADVANCE_CLOCK_INTERVAL) && ATOMIC_BCAS(&last_advance_clock_ts_us_, old_ts, cur_ts)) {
|
||||||
|
if (enable_adaptive_limit_) {
|
||||||
|
bool is_updated = false;
|
||||||
|
ALLOCATOR::adaptive_update_limit(
|
||||||
|
tenant_id_, holding_size, config_specify_resource_limit_, resource_limit_, last_update_limit_ts_, is_updated);
|
||||||
|
if (is_updated) {
|
||||||
|
(void)update_decay_factor_(true /* is_adaptive_update */);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool unused = false;
|
bool unused = false;
|
||||||
const int64_t throttle_trigger = resource_limit_ * throttle_trigger_percentage_ / 100;
|
const int64_t throttle_trigger = resource_limit_ * throttle_trigger_percentage_ / 100;
|
||||||
const int64_t avaliable_resource = avaliable_resource_after_dt_(holding_size, throttle_trigger, advance_us);
|
const int64_t avaliable_resource = avaliable_resource_after_dt_(holding_size, throttle_trigger, advance_us);
|
||||||
@ -490,6 +501,12 @@ void ObThrottleUnit<ALLOCATOR>::update_throttle_config(const int64_t resource_li
|
|||||||
throttle_trigger_percentage_ != throttle_trigger_percentage ||
|
throttle_trigger_percentage_ != throttle_trigger_percentage ||
|
||||||
throttle_max_duration_ != throttle_max_duration) {
|
throttle_max_duration_ != throttle_max_duration) {
|
||||||
config_changed = true;
|
config_changed = true;
|
||||||
|
SHARE_LOG(INFO,
|
||||||
|
"[Throttle] Update Config",
|
||||||
|
THROTTLE_UNIT_INFO,
|
||||||
|
"New Resource Limit(MB)", resource_limit / 1024 / 1024,
|
||||||
|
"New trigger percentage", throttle_trigger_percentage,
|
||||||
|
"New Throttle Duration", throttle_max_duration);
|
||||||
throttle_trigger_percentage_ = throttle_trigger_percentage;
|
throttle_trigger_percentage_ = throttle_trigger_percentage;
|
||||||
throttle_max_duration_ = throttle_max_duration;
|
throttle_max_duration_ = throttle_max_duration;
|
||||||
(void)inner_set_resource_limit_(resource_limit);
|
(void)inner_set_resource_limit_(resource_limit);
|
||||||
|
|||||||
Reference in New Issue
Block a user