commit 78d6463f81f86f303f1dbdc52714b5f26b9fc471
Author: Vladislavs Sokurenko <vladislavs.sokurenko@zabbix.com>
Date:   Thu Apr 29 15:31:12 2021 +0300

    .......PS. [ZBX-19038] cherry picked from 5.0; fixed conflict in ipmi_manager.c

diff --git a/include/preproc.h b/include/preproc.h
index 6a011cbd71..b9cde38d2a 100644
--- a/include/preproc.h
+++ b/include/preproc.h
@@ -43,7 +43,7 @@ zbx_preproc_item_stats_t;
 
 /* the following functions are implemented differently for server and proxy */
 
-void	zbx_preprocess_item_value(zbx_uint64_t itemid, unsigned char item_value_type, unsigned char item_flags,
+void	zbx_preprocess_item_value(zbx_uint64_t itemid, zbx_uint64_t hostid, unsigned char item_value_type, unsigned char item_flags,
 		AGENT_RESULT *result, zbx_timespec_t *ts, unsigned char state, char *error);
 void	zbx_preprocessor_flush(void);
 zbx_uint64_t	zbx_preprocessor_get_queue_size(void);
diff --git a/include/zbxlld.h b/include/zbxlld.h
index 6df122d99e..f4e3068bfe 100644
--- a/include/zbxlld.h
+++ b/include/zbxlld.h
@@ -23,10 +23,10 @@
 #include "common.h"
 #include "zbxalgo.h"
 
-void	zbx_lld_process_value(zbx_uint64_t itemid, const char *value, const zbx_timespec_t *ts, unsigned char meta,
+void	zbx_lld_process_value(zbx_uint64_t itemid, zbx_uint64_t hostid, const char *value, const zbx_timespec_t *ts, unsigned char meta,
 		zbx_uint64_t lastlogsize, int mtime, const char *error);
 
-void	zbx_lld_process_agent_result(zbx_uint64_t itemid, AGENT_RESULT *result, zbx_timespec_t *ts, char *error);
+void	zbx_lld_process_agent_result(zbx_uint64_t itemid, zbx_uint64_t hostid, AGENT_RESULT *result, zbx_timespec_t *ts, char *error);
 
 int	zbx_lld_get_queue_size(zbx_uint64_t *size, char **error);
 
diff --git a/src/libs/zbxdbcache/dbconfig.c b/src/libs/zbxdbcache/dbconfig.c
index 177f9fd8de..c6b7abf625 100644
--- a/src/libs/zbxdbcache/dbconfig.c
+++ b/src/libs/zbxdbcache/dbconfig.c
@@ -2834,9 +2834,15 @@ static void	DCsync_items(zbx_dbsync_t *sync, int flags)
 			{
 				item_hk_local.hostid = item->hostid;
 				item_hk_local.key = item->key;
-				item_hk = (ZBX_DC_ITEM_HK *)zbx_hashset_search(&config->items_hk, &item_hk_local);
 
-				if (item == item_hk->item_ptr)
+				if (NULL == (item_hk = (ZBX_DC_ITEM_HK *)zbx_hashset_search(&config->items_hk,
+						&item_hk_local)))
+				{
+					/* item keys should be unique for items within a host, otherwise items with  */
+					/* same key share index and removal of last added item already cleared index */
+					THIS_SHOULD_NEVER_HAPPEN;
+				}
+				else if (item == item_hk->item_ptr)
 				{
 					zbx_strpool_release(item_hk->key);
 					zbx_hashset_remove_direct(&config->items_hk, item_hk);
@@ -3521,9 +3527,14 @@ static void	DCsync_items(zbx_dbsync_t *sync, int flags)
 
 		item_hk_local.hostid = item->hostid;
 		item_hk_local.key = item->key;
-		item_hk = (ZBX_DC_ITEM_HK *)zbx_hashset_search(&config->items_hk, &item_hk_local);
 
-		if (item == item_hk->item_ptr)
+		if (NULL == (item_hk = (ZBX_DC_ITEM_HK *)zbx_hashset_search(&config->items_hk, &item_hk_local)))
+		{
+			/* item keys should be unique for items within a host, otherwise items with  */
+			/* same key share index and removal of last added item already cleared index */
+			THIS_SHOULD_NEVER_HAPPEN;
+		}
+		else if (item == item_hk->item_ptr)
 		{
 			zbx_strpool_release(item_hk->key);
 			zbx_hashset_remove_direct(&config->items_hk, item_hk);
diff --git a/src/libs/zbxdbhigh/proxy.c b/src/libs/zbxdbhigh/proxy.c
index 0a89bc472a..89bd169b70 100644
--- a/src/libs/zbxdbhigh/proxy.c
+++ b/src/libs/zbxdbhigh/proxy.c
@@ -3001,14 +3001,15 @@ static void	process_item_value(const DC_ITEM *item, AGENT_RESULT *result, zbx_ti
 {
 	if (0 == item->host.proxy_hostid)
 	{
-		zbx_preprocess_item_value(item->itemid, item->value_type, item->flags, result, ts, item->state, error);
+		zbx_preprocess_item_value(item->itemid, item->host.hostid, item->value_type, item->flags, result, ts,
+				item->state, error);
 		*h_num = 0;
 	}
 	else
 	{
 		if (0 != (ZBX_FLAG_DISCOVERY_RULE & item->flags))
 		{
-			zbx_lld_process_agent_result(item->itemid, result, ts, error);
+			zbx_lld_process_agent_result(item->itemid, item->host.hostid, result, ts, error);
 			*h_num = 0;
 		}
 		else
diff --git a/src/zabbix_proxy/proxy_lld.c b/src/zabbix_proxy/proxy_lld.c
index 1d61a9bc18..0de6c424f1 100644
--- a/src/zabbix_proxy/proxy_lld.c
+++ b/src/zabbix_proxy/proxy_lld.c
@@ -21,9 +21,10 @@
 #include "sysinfo.h"
 #include "zbxlld.h"
 
-void	zbx_lld_process_agent_result(zbx_uint64_t itemid, AGENT_RESULT *result, zbx_timespec_t *ts, char *error)
+void	zbx_lld_process_agent_result(zbx_uint64_t itemid, zbx_uint64_t hostid, AGENT_RESULT *result, zbx_timespec_t *ts, char *error)
 {
 	ZBX_UNUSED(itemid);
+	ZBX_UNUSED(hostid);
 	ZBX_UNUSED(result);
 	ZBX_UNUSED(ts);
 	ZBX_UNUSED(error);
diff --git a/src/zabbix_server/httppoller/httptest.c b/src/zabbix_server/httppoller/httptest.c
index 12a60857e1..3ca99a9dd4 100644
--- a/src/zabbix_server/httppoller/httptest.c
+++ b/src/zabbix_server/httppoller/httptest.c
@@ -189,8 +189,8 @@ static void	process_test_data(zbx_uint64_t httptestid, int lastfailedstep, doubl
 			}
 
 			items[i].state = ITEM_STATE_NORMAL;
-			zbx_preprocess_item_value(items[i].itemid, items[i].value_type, 0, &value, ts, items[i].state,
-					NULL);
+			zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type, 0, &value,
+					ts, items[i].state, NULL);
 
 			free_result(&value);
 		}
@@ -332,8 +332,8 @@ static void	process_step_data(zbx_uint64_t httpstepid, zbx_httpstat_t *stat, zbx
 			}
 
 			items[i].state = ITEM_STATE_NORMAL;
-			zbx_preprocess_item_value(items[i].itemid, items[i].value_type, 0, &value, ts, items[i].state,
-					NULL);
+			zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type, 0, &value,
+					ts, items[i].state, NULL);
 
 			free_result(&value);
 		}
diff --git a/src/zabbix_server/ipmi/ipmi_manager.c b/src/zabbix_server/ipmi/ipmi_manager.c
index e76267b7f5..3f8431e140 100644
--- a/src/zabbix_server/ipmi/ipmi_manager.c
+++ b/src/zabbix_server/ipmi/ipmi_manager.c
@@ -788,8 +788,8 @@ static int	ipmi_manager_schedule_requests(zbx_ipmi_manager_t *manager, int now,
 		{
 
 			zbx_timespec(&ts);
-			zbx_preprocess_item_value(items[i].itemid, items[i].value_type, items[i].flags, NULL, &ts,
-					state, error);
+			zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type,
+					items[i].flags, NULL, &ts, state, error);
 			DCrequeue_items(&items[i].itemid, &ts.sec, &errcode, 1);
 			zbx_free(error);
 			continue;
@@ -943,8 +943,8 @@ static void	ipmi_manager_process_value_result(zbx_ipmi_manager_t *manager, zbx_i
 				init_result(&result);
 				SET_TEXT_RESULT(&result, value);
 				value = NULL;
-				zbx_preprocess_item_value(itemid, ITEM_VALUE_TYPE_TEXT, flags, &result, &ts, state,
-						NULL);
+				zbx_preprocess_item_value(itemid, poller->request->hostid, ITEM_VALUE_TYPE_TEXT, flags,
+						&result, &ts, state, NULL);
 				free_result(&result);
 			}
 			break;
@@ -953,7 +953,8 @@ static void	ipmi_manager_process_value_result(zbx_ipmi_manager_t *manager, zbx_i
 		case AGENT_ERROR:
 		case CONFIG_ERROR:
 			state = ITEM_STATE_NOTSUPPORTED;
-			zbx_preprocess_item_value(itemid, ITEM_VALUE_TYPE_TEXT, flags, NULL, &ts, state, value);
+			zbx_preprocess_item_value(itemid, poller->request->hostid, ITEM_VALUE_TYPE_TEXT, flags, NULL,
+					&ts, state, value);
 			break;
 		default:
 			/* don't change item's state when network related error occurs */
diff --git a/src/zabbix_server/lld/lld_manager.c b/src/zabbix_server/lld/lld_manager.c
index 9f545f6300..0b428f4644 100644
--- a/src/zabbix_server/lld/lld_manager.c
+++ b/src/zabbix_server/lld/lld_manager.c
@@ -301,7 +301,7 @@ static void	lld_register_worker(zbx_lld_manager_t *manager, zbx_ipc_client_t *cl
  ******************************************************************************/
 static void	lld_queue_rule(zbx_lld_manager_t *manager, zbx_lld_rule_t *rule)
 {
-	zbx_binary_heap_elem_t	elem = {rule->itemid, rule};
+	zbx_binary_heap_elem_t	elem = {rule->hostid, rule};
 
 	zbx_binary_heap_insert(&manager->rule_queue, &elem);
 }
@@ -318,7 +318,7 @@ static void	lld_queue_rule(zbx_lld_manager_t *manager, zbx_lld_rule_t *rule)
  ******************************************************************************/
 static void	lld_queue_request(zbx_lld_manager_t *manager, const zbx_ipc_message_t *message)
 {
-	zbx_uint64_t	itemid;
+	zbx_uint64_t	hostid;
 	zbx_lld_rule_t	*rule;
 	zbx_lld_data_t	*data;
 
@@ -326,32 +326,49 @@ static void	lld_queue_request(zbx_lld_manager_t *manager, const zbx_ipc_message_
 
 	data = (zbx_lld_data_t *)zbx_malloc(NULL, sizeof(zbx_lld_data_t));
 	data->next = NULL;
-	zbx_lld_deserialize_item_value(message->data, &itemid, &data->value, &data->ts, &data->meta, &data->lastlogsize,
-			&data->mtime, &data->error);
 
-	if (NULL == (rule = zbx_hashset_search(&manager->rule_index, &itemid)))
+	zbx_lld_deserialize_item_value(message->data, &data->itemid, &hostid, &data->value, &data->ts, &data->meta,
+			&data->lastlogsize, &data->mtime, &data->error);
+
+	if (NULL == (rule = zbx_hashset_search(&manager->rule_index, &hostid)))
 	{
-		zbx_lld_rule_t	rule_local = {itemid, 0, data, data};
+		zbx_lld_rule_t	rule_local = {.hostid = hostid, .values_num = 0, .tail = data, .head = data};
+
+		data->prev = NULL;
 
 		rule = zbx_hashset_insert(&manager->rule_index, &rule_local, sizeof(rule_local));
 		lld_queue_rule(manager, rule);
 	}
 	else
 	{
-		if (0 == data->meta && 0 == zbx_strcmp_null(data->error, rule->tail->error) &&
-				0 == zbx_strcmp_null(data->value, rule->tail->value))
+		if (0 == data->meta)
 		{
-			zabbix_log(LOG_LEVEL_DEBUG, "skip repeating discovery rule values: " ZBX_FS_UI64, itemid);
+			zbx_lld_data_t	*data_ptr;
+
+			for (data_ptr = rule->tail; NULL != data_ptr; data_ptr = data_ptr->prev)
+			{
+				/* if there are multiple values then they should be different, check only last one */
+				if (data_ptr->itemid == data->itemid)
+					break;
+			}
+
+			if (NULL != data_ptr && 0 == zbx_strcmp_null(data->error, data_ptr->error) &&
+					0 == zbx_strcmp_null(data->value, data_ptr->value))
+			{
+				zabbix_log(LOG_LEVEL_DEBUG, "skip repeating values for discovery rule:" ZBX_FS_UI64,
+						data->itemid);
 
-			lld_data_free(data);
-			goto out;
+				lld_data_free(data);
+				goto out;
+			}
 		}
 
+		data->prev = rule->tail;
 		rule->tail->next = data;
 		rule->tail = data;
 	}
 
-	zabbix_log(LOG_LEVEL_DEBUG, "queuing discovery rule: " ZBX_FS_UI64, itemid);
+	zabbix_log(LOG_LEVEL_DEBUG, "queuing discovery rule:" ZBX_FS_UI64, data->itemid);
 
 	rule->values_num++;
 	manager->queued_num++;
@@ -381,7 +398,7 @@ static void	lld_process_next_request(zbx_lld_manager_t *manager, zbx_lld_worker_
 	zbx_binary_heap_remove_min(&manager->rule_queue);
 
 	data = worker->rule->head;
-	buf_len = zbx_lld_serialize_item_value(&buf, worker->rule->itemid, data->value, &data->ts, data->meta,
+	buf_len = zbx_lld_serialize_item_value(&buf, data->itemid, 0, data->value, &data->ts, data->meta,
 			data->lastlogsize, data->mtime, data->error);
 	zbx_ipc_client_send(worker->client, ZBX_IPC_LLD_TASK, buf, buf_len);
 	zbx_free(buf);
@@ -429,7 +446,7 @@ static void	lld_process_result(zbx_lld_manager_t *manager, zbx_ipc_client_t *cli
 
 	worker = lld_get_worker_by_client(manager, client);
 
-	zabbix_log(LOG_LEVEL_DEBUG, "discovery rule:" ZBX_FS_UI64 " has been processed", worker->rule->itemid);
+	zabbix_log(LOG_LEVEL_DEBUG, "discovery rule:" ZBX_FS_UI64 " has been processed", worker->rule->head->itemid);
 
 	rule = worker->rule;
 	worker->rule = NULL;
@@ -443,6 +460,7 @@ static void	lld_process_result(zbx_lld_manager_t *manager, zbx_ipc_client_t *cli
 	}
 	else
 	{
+		rule->head->prev = NULL;
 		rule->values_num--;
 		lld_queue_rule(manager, rule);
 	}
@@ -487,8 +505,8 @@ static void	lld_process_diag_stats(zbx_lld_manager_t *manager, zbx_ipc_client_t
  ******************************************************************************/
 static int	lld_diag_item_compare_values_desc(const void *d1, const void *d2)
 {
-	zbx_lld_rule_t	*r1 = *(zbx_lld_rule_t **)d1;
-	zbx_lld_rule_t	*r2 = *(zbx_lld_rule_t **)d2;
+	zbx_lld_rule_info_t	*r1 = *(zbx_lld_rule_info_t **)d1;
+	zbx_lld_rule_info_t	*r2 = *(zbx_lld_rule_info_t **)d2;
 
 	return r2->values_num - r1->values_num;
 }
@@ -512,27 +530,48 @@ static void	lld_process_top_items(zbx_lld_manager_t *manager, zbx_ipc_client_t *
 	zbx_uint32_t		data_len;
 	zbx_vector_ptr_t	view;
 	zbx_hashset_iter_t	iter;
-	zbx_lld_rule_t		*item;
-	int			items_num;
+	zbx_hashset_t		rule_infos;
+	zbx_lld_rule_t		*rule;
 
 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
 
 	zbx_lld_deserialize_top_items_request(message->data, &limit);
 
+	zbx_hashset_create(&rule_infos, MAX(1000, (size_t)manager->rule_index.num_data), ZBX_DEFAULT_UINT64_HASH_FUNC,
+			ZBX_DEFAULT_UINT64_COMPARE_FUNC);
 	zbx_vector_ptr_create(&view);
 
 	zbx_hashset_iter_reset(&manager->rule_index, &iter);
-	while (NULL != (item = (zbx_lld_rule_t *)zbx_hashset_iter_next(&iter)))
-		zbx_vector_ptr_append(&view, item);
+	while (NULL != (rule = (zbx_lld_rule_t *)zbx_hashset_iter_next(&iter)))
+	{
+		zbx_lld_data_t	*data_ptr;
+
+		for (data_ptr = rule->head; NULL != data_ptr; data_ptr = data_ptr->next)
+		{
+			zbx_lld_rule_info_t	*rule_info, rule_info_local = {.itemid = data_ptr->itemid};
+
+			rule_info = (zbx_lld_rule_info_t *)zbx_hashset_search(&rule_infos, &rule_info_local);
+
+			if (NULL == rule_info)
+			{
+				rule_info = (zbx_lld_rule_info_t *)zbx_hashset_insert(&rule_infos, &rule_info_local,
+						sizeof(zbx_lld_rule_info_t));
+				zbx_vector_ptr_append(&view, rule_info);
+			}
+
+			rule_info->values_num++;
+		}
+	}
 
 	zbx_vector_ptr_sort(&view, lld_diag_item_compare_values_desc);
-	items_num = MIN(limit, view.values_num);
 
-	data_len = zbx_lld_serialize_top_items_result(&data, (zbx_lld_rule_t **)view.values, items_num);
+	data_len = zbx_lld_serialize_top_items_result(&data, (const zbx_lld_rule_info_t **)view.values,
+			MIN(limit, view.values_num));
 	zbx_ipc_client_send(client, ZBX_IPC_LLD_TOP_ITEMS_RESULT, data, data_len);
 
 	zbx_free(data);
 	zbx_vector_ptr_destroy(&view);
+	zbx_hashset_destroy(&rule_infos);
 
 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
 }
diff --git a/src/zabbix_server/lld/lld_manager.h b/src/zabbix_server/lld/lld_manager.h
index 9fdd7243c1..52817ceaf7 100644
--- a/src/zabbix_server/lld/lld_manager.h
+++ b/src/zabbix_server/lld/lld_manager.h
@@ -24,6 +24,9 @@
 
 typedef struct zbx_lld_value
 {
+	/* the LLD rule id */
+	zbx_uint64_t		itemid;
+
 	char			*value;
 	char			*error;
 	zbx_timespec_t		ts;
@@ -31,28 +34,38 @@ typedef struct zbx_lld_value
 	zbx_uint64_t		lastlogsize;
 	int			mtime;
 	unsigned char		meta;
-
+	struct	zbx_lld_value	*prev;
 	struct	zbx_lld_value	*next;
 }
 zbx_lld_data_t;
 
-/* queue of values for one LLD rule */
+/* queue of values for one host */
 typedef struct
 {
-	/* the LLD rule id */
-	zbx_uint64_t	itemid;
+	/* the LLD rule host id */
+	zbx_uint64_t	hostid;
 
 	/* the number of queued values */
 	int		values_num;
 
-	/* the oldest value in queue */
+	/* the newest value in queue */
 	zbx_lld_data_t	*tail;
 
-	/* the newest value in queue */
+	/* the oldest value in queue */
 	zbx_lld_data_t	*head;
 }
 zbx_lld_rule_t;
 
+typedef struct
+{
+	/* the LLD rule item id */
+	zbx_uint64_t	itemid;
+
+	/* the number of queued values */
+	int		values_num;
+}
+zbx_lld_rule_info_t;
+
 ZBX_THREAD_ENTRY(lld_manager_thread, args);
 
 #endif
diff --git a/src/zabbix_server/lld/lld_protocol.c b/src/zabbix_server/lld/lld_protocol.c
index d852c0a9ea..adf6130f1f 100644
--- a/src/zabbix_server/lld/lld_protocol.c
+++ b/src/zabbix_server/lld/lld_protocol.c
@@ -31,13 +31,15 @@
  * Function: zbx_lld_serialize_item_value                                     *
  *                                                                            *
  ******************************************************************************/
-zbx_uint32_t	zbx_lld_serialize_item_value(unsigned char **data, zbx_uint64_t itemid, const char *value,
-		const zbx_timespec_t *ts, unsigned char meta, zbx_uint64_t lastlogsize, int mtime, const char *error)
+zbx_uint32_t	zbx_lld_serialize_item_value(unsigned char **data, zbx_uint64_t itemid, zbx_uint64_t hostid,
+		const char *value, const zbx_timespec_t *ts, unsigned char meta, zbx_uint64_t lastlogsize, int mtime,
+		const char *error)
 {
 	unsigned char	*ptr;
 	zbx_uint32_t	data_len = 0, value_len, error_len;
 
 	zbx_serialize_prepare_value(data_len, itemid);
+	zbx_serialize_prepare_value(data_len, hostid);
 	zbx_serialize_prepare_str(data_len, value);
 	zbx_serialize_prepare_value(data_len, *ts);
 	zbx_serialize_prepare_str(data_len, error);
@@ -53,6 +55,7 @@ zbx_uint32_t	zbx_lld_serialize_item_value(unsigned char **data, zbx_uint64_t ite
 
 	ptr = *data;
 	ptr += zbx_serialize_value(ptr, itemid);
+	ptr += zbx_serialize_value(ptr, hostid);
 	ptr += zbx_serialize_str(ptr, value, value_len);
 	ptr += zbx_serialize_value(ptr, *ts);
 	ptr += zbx_serialize_str(ptr, error, error_len);
@@ -71,12 +74,14 @@ zbx_uint32_t	zbx_lld_serialize_item_value(unsigned char **data, zbx_uint64_t ite
  * Function: zbx_lld_deserialize_item_value                                   *
  *                                                                            *
  ******************************************************************************/
-void	zbx_lld_deserialize_item_value(const unsigned char *data, zbx_uint64_t *itemid, char **value,
-		zbx_timespec_t *ts, unsigned char *meta, zbx_uint64_t *lastlogsize, int *mtime, char **error)
+void	zbx_lld_deserialize_item_value(const unsigned char *data, zbx_uint64_t *itemid, zbx_uint64_t *hostid,
+		char **value, zbx_timespec_t *ts, unsigned char *meta, zbx_uint64_t *lastlogsize, int *mtime,
+		char **error)
 {
 	zbx_uint32_t	value_len, error_len;
 
 	data += zbx_deserialize_value(data, itemid);
+	data += zbx_deserialize_value(data, hostid);
 	data += zbx_deserialize_str(data, value, value_len);
 	data += zbx_deserialize_value(data, ts);
 	data += zbx_deserialize_str(data, error, error_len);
@@ -153,29 +158,30 @@ void	zbx_lld_deserialize_top_items_request(const unsigned char *data, int *limit
  * Function: zbx_lld_serialize_top_items_result                               *
  *                                                                            *
  ******************************************************************************/
-zbx_uint32_t	zbx_lld_serialize_top_items_result(unsigned char **data, zbx_lld_rule_t **items, int items_num)
+zbx_uint32_t	zbx_lld_serialize_top_items_result(unsigned char **data, const zbx_lld_rule_info_t **rule_infos,
+		int num)
 {
 	unsigned char	*ptr;
 	zbx_uint32_t	data_len = 0, item_len = 0;
 	int		i;
 
-	if (0 != items_num)
+	if (0 != num)
 	{
-		zbx_serialize_prepare_value(item_len, items[0]->itemid);
-		zbx_serialize_prepare_value(item_len, items[0]->values_num);
+		zbx_serialize_prepare_value(item_len, rule_infos[0]->itemid);
+		zbx_serialize_prepare_value(item_len, rule_infos[0]->values_num);
 	}
 
-	zbx_serialize_prepare_value(data_len, items_num);
-	data_len += item_len * items_num;
+	zbx_serialize_prepare_value(data_len, num);
+	data_len += item_len * num;
 	*data = (unsigned char *)zbx_malloc(NULL, data_len);
 
 	ptr = *data;
-	ptr += zbx_serialize_value(ptr, items_num);
+	ptr += zbx_serialize_value(ptr, num);
 
-	for (i = 0; i < items_num; i++)
+	for (i = 0; i < num; i++)
 	{
-		ptr += zbx_serialize_value(ptr, items[i]->itemid);
-		ptr += zbx_serialize_value(ptr, items[i]->values_num);
+		ptr += zbx_serialize_value(ptr, rule_infos[i]->itemid);
+		ptr += zbx_serialize_value(ptr, rule_infos[i]->values_num);
 	}
 
 	return data_len;
@@ -216,13 +222,14 @@ static void	zbx_lld_deserialize_top_items_result(const unsigned char *data, zbx_
  * Purpose: process low level discovery value/error                           *
  *                                                                            *
  * Parameters: itemid - [IN] the LLD rule id                                  *
+ *             hostid - [IN] the host id                                      *
  *             value  - [IN] the rule value (can be NULL if error is set)     *
  *             ts     - [IN] the value timestamp                              *
  *             error  - [IN] the error message (can be NULL)                  *
  *                                                                            *
  ******************************************************************************/
-void	zbx_lld_process_value(zbx_uint64_t itemid, const char *value, const zbx_timespec_t *ts, unsigned char meta,
-		zbx_uint64_t lastlogsize, int mtime, const char *error)
+void	zbx_lld_process_value(zbx_uint64_t itemid, zbx_uint64_t hostid, const char *value, const zbx_timespec_t *ts,
+		unsigned char meta, zbx_uint64_t lastlogsize, int mtime, const char *error)
 {
 	static zbx_ipc_socket_t	socket;
 	char			*errmsg = NULL;
@@ -236,7 +243,7 @@ void	zbx_lld_process_value(zbx_uint64_t itemid, const char *value, const zbx_tim
 		exit(EXIT_FAILURE);
 	}
 
-	data_len = zbx_lld_serialize_item_value(&data, itemid, value, ts, meta, lastlogsize, mtime, error);
+	data_len = zbx_lld_serialize_item_value(&data, itemid, hostid, value, ts, meta, lastlogsize, mtime, error);
 
 	if (FAIL == zbx_ipc_socket_write(&socket, ZBX_IPC_LLD_REQUEST, data, data_len))
 	{
@@ -254,12 +261,14 @@ void	zbx_lld_process_value(zbx_uint64_t itemid, const char *value, const zbx_tim
  * Purpose: process low level discovery agent result                          *
  *                                                                            *
  * Parameters: itemid - [IN] the LLD rule id                                  *
+ *             hostid - [IN] the host id                                      *
  *             result - [IN] the agent result                                 *
  *             ts     - [IN] the value timestamp                              *
  *             error  - [IN] the error message (can be NULL)                  *
  *                                                                            *
  ******************************************************************************/
-void	zbx_lld_process_agent_result(zbx_uint64_t itemid, AGENT_RESULT *result, zbx_timespec_t *ts, char *error)
+void	zbx_lld_process_agent_result(zbx_uint64_t itemid, zbx_uint64_t hostid, AGENT_RESULT *result,
+		zbx_timespec_t *ts, char *error)
 {
 	const char	*value = NULL;
 	unsigned char	meta = 0;
@@ -280,7 +289,7 @@ void	zbx_lld_process_agent_result(zbx_uint64_t itemid, AGENT_RESULT *result, zbx
 	}
 
 	if (NULL != value || NULL != error || 0 != meta)
-		zbx_lld_process_value(itemid, value, ts, meta, lastlogsize, mtime, error);
+		zbx_lld_process_value(itemid, hostid, value, ts, meta, lastlogsize, mtime, error);
 }
 
 /******************************************************************************
diff --git a/src/zabbix_server/lld/lld_protocol.h b/src/zabbix_server/lld/lld_protocol.h
index c21fbe48a0..21fabccde4 100644
--- a/src/zabbix_server/lld/lld_protocol.h
+++ b/src/zabbix_server/lld/lld_protocol.h
@@ -51,16 +51,19 @@
 #define ZBX_IPC_LLD_TOP_ITEMS_RESULT	1403
 
 
-zbx_uint32_t	zbx_lld_serialize_item_value(unsigned char **data, zbx_uint64_t itemid, const char *value,
-		const zbx_timespec_t *ts, unsigned char meta, zbx_uint64_t lastlogsize, int mtime, const char *error);
+zbx_uint32_t	zbx_lld_serialize_item_value(unsigned char **data, zbx_uint64_t itemid, zbx_uint64_t hostid,
+		const char *value, const zbx_timespec_t *ts, unsigned char meta, zbx_uint64_t lastlogsize, int mtime,
+		const char *error);
 
-void	zbx_lld_deserialize_item_value(const unsigned char *data, zbx_uint64_t *itemid, char **value,
-		zbx_timespec_t *ts, unsigned char *meta, zbx_uint64_t *lastlogsize, int *mtime, char **error);
+void	zbx_lld_deserialize_item_value(const unsigned char *data, zbx_uint64_t *itemid, zbx_uint64_t *hostid,
+		char **value, zbx_timespec_t *ts, unsigned char *meta, zbx_uint64_t *lastlogsize, int *mtime,
+		char **error);
 
 zbx_uint32_t	zbx_lld_serialize_diag_stats(unsigned char **data, zbx_uint64_t items_num, zbx_uint64_t values_num);
 
 void	zbx_lld_deserialize_top_items_request(const unsigned char *data, int *limit);
 
-zbx_uint32_t	zbx_lld_serialize_top_items_result(unsigned char **data, zbx_lld_rule_t **items, int items_num);
+zbx_uint32_t	zbx_lld_serialize_top_items_result(unsigned char **data, const zbx_lld_rule_info_t **rule_infos,
+		int num);
 
 #endif
diff --git a/src/zabbix_server/lld/lld_worker.c b/src/zabbix_server/lld/lld_worker.c
index 51b3d25b44..4d46669e4d 100644
--- a/src/zabbix_server/lld/lld_worker.c
+++ b/src/zabbix_server/lld/lld_worker.c
@@ -63,7 +63,7 @@ static void	lld_register_worker(zbx_ipc_socket_t *socket)
  ******************************************************************************/
 static void	lld_process_task(zbx_ipc_message_t *message)
 {
-	zbx_uint64_t		itemid, lastlogsize;
+	zbx_uint64_t		itemid, hostid, lastlogsize;
 	char			*value, *error;
 	zbx_timespec_t		ts;
 	zbx_item_diff_t		diff;
@@ -73,7 +73,7 @@ static void	lld_process_task(zbx_ipc_message_t *message)
 
 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
 
-	zbx_lld_deserialize_item_value(message->data, &itemid, &value, &ts, &meta, &lastlogsize, &mtime, &error);
+	zbx_lld_deserialize_item_value(message->data, &itemid, &hostid, &value, &ts, &meta, &lastlogsize, &mtime, &error);
 
 	DCconfig_get_items_by_itemids(&item, &itemid, &errcode, 1);
 	if (SUCCEED != errcode)
diff --git a/src/zabbix_server/pinger/pinger.c b/src/zabbix_server/pinger/pinger.c
index ff9a322079..86baab12a2 100644
--- a/src/zabbix_server/pinger/pinger.c
+++ b/src/zabbix_server/pinger/pinger.c
@@ -78,7 +78,8 @@ static void	process_value(zbx_uint64_t itemid, zbx_uint64_t *value_ui64, double
 	if (NOTSUPPORTED == ping_result)
 	{
 		item.state = ITEM_STATE_NOTSUPPORTED;
-		zbx_preprocess_item_value(item.itemid, item.value_type, item.flags, NULL, ts, item.state, error);
+		zbx_preprocess_item_value(item.itemid, item.host.hostid, item.value_type, item.flags, NULL, ts,
+				item.state, error);
 	}
 	else
 	{
@@ -90,7 +91,8 @@ static void	process_value(zbx_uint64_t itemid, zbx_uint64_t *value_ui64, double
 			SET_DBL_RESULT(&value, *value_dbl);
 
 		item.state = ITEM_STATE_NORMAL;
-		zbx_preprocess_item_value(item.itemid, item.value_type, item.flags, &value, ts, item.state, NULL);
+		zbx_preprocess_item_value(item.itemid, item.host.hostid, item.value_type, item.flags, &value, ts,
+				item.state, NULL);
 
 		free_result(&value);
 	}
@@ -442,8 +444,8 @@ static void	get_pinger_hosts(icmpitem_t **icmp_items, int *icmp_items_alloc, int
 			zbx_timespec(&ts);
 
 			items[i].state = ITEM_STATE_NOTSUPPORTED;
-			zbx_preprocess_item_value(items[i].itemid, items[i].value_type, items[i].flags, NULL, &ts,
-					items[i].state, error);
+			zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type,
+					items[i].flags, NULL, &ts, items[i].state, error);
 
 			DCrequeue_items(&items[i].itemid, &ts.sec, &errcode, 1);
 		}
diff --git a/src/zabbix_server/poller/poller.c b/src/zabbix_server/poller/poller.c
index e819b96766..26e02d403a 100644
--- a/src/zabbix_server/poller/poller.c
+++ b/src/zabbix_server/poller/poller.c
@@ -908,8 +908,8 @@ static int	get_values(unsigned char poller_type, int *nextcheck)
 			if (0 == add_results.values_num)
 			{
 				items[i].state = ITEM_STATE_NORMAL;
-				zbx_preprocess_item_value(items[i].itemid, items[i].value_type, items[i].flags,
-						&results[i], &timespec, items[i].state, NULL);
+				zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type,
+						items[i].flags, &results[i], &timespec, items[i].state, NULL);
 			}
 			else
 			{
@@ -925,16 +925,16 @@ static int	get_values(unsigned char poller_type, int *nextcheck)
 					if (ISSET_MSG(add_result))
 					{
 						items[i].state = ITEM_STATE_NOTSUPPORTED;
-						zbx_preprocess_item_value(items[i].itemid, items[i].value_type,
-								items[i].flags, NULL, &ts_tmp, items[i].state,
+						zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid,
+						items[i].value_type, items[i].flags, NULL, &ts_tmp, items[i].state,
 								add_result->msg);
 					}
 					else
 					{
 						items[i].state = ITEM_STATE_NORMAL;
-						zbx_preprocess_item_value(items[i].itemid, items[i].value_type,
-								items[i].flags, add_result, &ts_tmp, items[i].state,
-								NULL);
+						zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid,
+								items[i].value_type, items[i].flags, add_result,
+								&ts_tmp, items[i].state, NULL);
 					}
 
 					/* ensure that every log item value timestamp is unique */
@@ -949,8 +949,8 @@ static int	get_values(unsigned char poller_type, int *nextcheck)
 		else if (NOTSUPPORTED == errcodes[i] || AGENT_ERROR == errcodes[i] || CONFIG_ERROR == errcodes[i])
 		{
 			items[i].state = ITEM_STATE_NOTSUPPORTED;
-			zbx_preprocess_item_value(items[i].itemid, items[i].value_type, items[i].flags, NULL, &timespec,
-					items[i].state, results[i].msg);
+			zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type,
+					items[i].flags, NULL, &timespec, items[i].state, results[i].msg);
 		}
 
 		DCpoller_requeue_items(&items[i].itemid, &timespec.sec, &errcodes[i], 1, poller_type,
diff --git a/src/zabbix_server/preprocessor/preproc_manager.c b/src/zabbix_server/preprocessor/preproc_manager.c
index db89eedae1..584be29256 100644
--- a/src/zabbix_server/preprocessor/preproc_manager.c
+++ b/src/zabbix_server/preprocessor/preproc_manager.c
@@ -519,7 +519,8 @@ static void	preprocessor_flush_value(const zbx_preproc_item_value_t *value)
 				value->ts, value->state, value->error);
 	}
 	else
-		zbx_lld_process_agent_result(value->itemid, value->result_ptr->result, value->ts, value->error);
+		zbx_lld_process_agent_result(value->itemid, value->hostid, value->result_ptr->result, value->ts,
+				value->error);
 }
 
 /******************************************************************************
diff --git a/src/zabbix_server/preprocessor/preprocessing.c b/src/zabbix_server/preprocessor/preprocessing.c
index 6d548b122c..b5031c0bed 100644
--- a/src/zabbix_server/preprocessor/preprocessing.c
+++ b/src/zabbix_server/preprocessor/preprocessing.c
@@ -136,13 +136,14 @@ static zbx_uint32_t	message_pack_data(zbx_ipc_message_t *message, zbx_packed_fie
  ******************************************************************************/
 static zbx_uint32_t	preprocessor_pack_value(zbx_ipc_message_t *message, zbx_preproc_item_value_t *value)
 {
-	zbx_packed_field_t	fields[23], *offset = fields;	/* 23 - max field count */
+	zbx_packed_field_t	fields[24], *offset = fields;	/* 24 - max field count */
 	unsigned char		ts_marker, result_marker, log_marker;
 
 	ts_marker = (NULL != value->ts);
 	result_marker = (NULL != value->result_ptr->result);
 
 	*offset++ = PACKED_FIELD(&value->itemid, sizeof(zbx_uint64_t));
+	*offset++ = PACKED_FIELD(&value->hostid, sizeof(zbx_uint64_t));
 	*offset++ = PACKED_FIELD(&value->item_value_type, sizeof(unsigned char));
 	*offset++ = PACKED_FIELD(&value->item_flags, sizeof(unsigned char));
 	*offset++ = PACKED_FIELD(&value->state, sizeof(unsigned char));
@@ -723,6 +724,7 @@ zbx_uint32_t	zbx_preprocessor_unpack_value(zbx_preproc_item_value_t *value, unsi
 	unsigned char	*offset = data, ts_marker, result_marker, log_marker;
 
 	offset += zbx_deserialize_uint64(offset, &value->itemid);
+	offset += zbx_deserialize_uint64(offset, &value->hostid);
 	offset += zbx_deserialize_char(offset, &value->item_value_type);
 	offset += zbx_deserialize_char(offset, &value->item_flags);
 	offset += zbx_deserialize_char(offset, &value->state);
@@ -992,9 +994,10 @@ static void	preprocessor_send(zbx_uint32_t code, unsigned char *data, zbx_uint32
  *                                                                            *
  * Function: zbx_preprocess_item_value                                        *
  *                                                                            *
- * Purpose: perform item value preprocessing and dependend item processing    *
+ * Purpose: perform item value preprocessing and dependent item processing    *
  *                                                                            *
  * Parameters: itemid          - [IN] the itemid                              *
+ *             itemid          - [IN] the hostid                              *
  *             item_value_type - [IN] the item value type                     *
  *             item_flags      - [IN] the item flags (e. g. lld rule)         *
  *             result          - [IN] agent result containing the value       *
@@ -1005,10 +1008,10 @@ static void	preprocessor_send(zbx_uint32_t code, unsigned char *data, zbx_uint32
  *                               ITEM_STATE_NOTSUPPORTED                      *
  *                                                                            *
  ******************************************************************************/
-void	zbx_preprocess_item_value(zbx_uint64_t itemid, unsigned char item_value_type, unsigned char item_flags,
-		AGENT_RESULT *result, zbx_timespec_t *ts, unsigned char state, char *error)
+void	zbx_preprocess_item_value(zbx_uint64_t itemid, zbx_uint64_t hostid, unsigned char item_value_type,
+		unsigned char item_flags, AGENT_RESULT *result, zbx_timespec_t *ts, unsigned char state, char *error)
 {
-	zbx_preproc_item_value_t	value = {.itemid = itemid, .item_value_type = item_value_type,
+	zbx_preproc_item_value_t	value = {.itemid = itemid, .hostid = hostid, .item_value_type = item_value_type,
 					.error = error, .item_flags = item_flags, .state = state, .ts = ts};
 	zbx_result_ptr_t		result_ptr = {.result = result};
 	size_t				value_len = 0, len;
diff --git a/src/zabbix_server/preprocessor/preprocessing.h b/src/zabbix_server/preprocessor/preprocessing.h
index 5d1bfd6bf8..bd0e5094f6 100644
--- a/src/zabbix_server/preprocessor/preprocessing.h
+++ b/src/zabbix_server/preprocessor/preprocessing.h
@@ -48,6 +48,7 @@ typedef struct {
 typedef struct
 {
 	zbx_uint64_t		itemid;		 /* item id */
+	zbx_uint64_t		hostid;		 /* host id */
 	unsigned char		item_value_type; /* item value type */
 	zbx_result_ptr_t	*result_ptr;	 /* item value (if any) to be shared between master and dependent items */
 	zbx_timespec_t		*ts;		 /* timestamp of a value */
diff --git a/src/zabbix_server/snmptrapper/snmptrapper.c b/src/zabbix_server/snmptrapper/snmptrapper.c
index d1b1a4e4e0..83b9c53528 100644
--- a/src/zabbix_server/snmptrapper/snmptrapper.c
+++ b/src/zabbix_server/snmptrapper/snmptrapper.c
@@ -188,7 +188,7 @@ next:
 				}
 
 				items[i].state = ITEM_STATE_NORMAL;
-				zbx_preprocess_item_value(items[i].itemid, items[i].value_type, items[i].flags,
+				zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type, items[i].flags,
 						&results[i], ts, items[i].state, NULL);
 
 				itemids[i] = items[i].itemid;
@@ -196,7 +196,7 @@ next:
 				break;
 			case NOTSUPPORTED:
 				items[i].state = ITEM_STATE_NOTSUPPORTED;
-				zbx_preprocess_item_value(items[i].itemid, items[i].value_type, items[i].flags, NULL,
+				zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type, items[i].flags, NULL,
 						ts, items[i].state, results[i].msg);
 
 				itemids[i] = items[i].itemid;
