Repository: celery/celery

Issue:
Celery 4.3 result_extended doesn't push extended meta data to some backends when set to true
<!--
Please fill this template entirely and do not erase parts of it.
We reserve the right to close without a response
bug reports which are incomplete.
-->
# Checklist
<!--
To check an item on the list replace [ ] with [x].
-->

- [x] I have read the relevant section in the
  [contribution guide](http://docs.celeryproject.org/en/latest/contributing.html#other-bugs)
  on reporting bugs.
- [x] I have checked the [issues list](https://github.com/celery/celery/issues?q=is%3Aissue+label%3A%22Issue+Type%3A+Bug+Report%22+-label%3A%22Category%3A+Documentation%22)
  for similar or identical bug reports.
- [x] I have checked the [pull requests list](https://github.com/celery/celery/pulls?q=is%3Apr+label%3A%22PR+Type%3A+Bugfix%22+-label%3A%22Category%3A+Documentation%22)
  for existing proposed fixes.
- [x] I have checked the [commit log](https://github.com/celery/celery/commits/master)
  to find out if the bug was already fixed in the master branch.
- [x] I have included all related issues and possible duplicate issues
  in this issue (If there are none, check this box anyway).

## Mandatory Debugging Information

- [x] I have included the output of ``celery -A proj report`` in the issue.
    (if you are not able to do this, then at least specify the Celery
     version affected).
- [x] I have verified that the issue exists against the `master` branch of Celery.
- [x] I have included the contents of ``pip freeze`` in the issue.
- [x] I have included all the versions of all the external dependencies required
  to reproduce this bug.

## Optional Debugging Information
<!--
Try some of the below if you think they are relevant.
It will help us figure out the scope of the bug and how many users it affects.
-->
- [ ] I have tried reproducing the issue on more than one Python version
  and/or implementation.
- [ ] I have tried reproducing the issue on more than one message broker and/or
  result backend.
- [ ] I have tried reproducing the issue on more than one version of the message
  broker and/or result backend.
- [ ] I have tried reproducing the issue on more than one operating system.
- [ ] I have tried reproducing the issue on more than one workers pool.
- [ ] I have tried reproducing the issue with autoscaling, retries,
  ETA/Countdown & rate limits disabled.
- [ ] I have tried reproducing the issue after downgrading
  and/or upgrading Celery and its dependencies.

## Related Issues and Possible Duplicates
<!--
Please make sure to search and mention any related issues
or possible duplicates to this issue as requested by the checklist above.

This may or may not include issues in other repositories that the Celery project
maintains or other repositories that are dependencies of Celery.

If you don't know how to mention issues, please refer to Github's documentation
on the subject: https://help.github.com/en/articles/autolinked-references-and-urls#issues-and-pull-requests
-->

#### Related Issues

- None

#### Possible Duplicates

- None

## Environment & Settings
<!-- Include the contents of celery --version below -->
**Celery version**:
<!-- Include the output of celery -A proj report below -->
<details>
<summary><b><code>celery report</code> Output:</b></summary>
<p>

```
celery -A celeryacl report

software -> celery:4.3.0 (rhubarb) kombu:4.5.0 py:3.6.8
billiard:3.6.0.0 py-amqp:2.4.2
platform -> system:Linux arch:64bit
kernel version:4.18.0-1013-azure imp:CPython
loader -> celery.loaders.app.AppLoader
settings -> transport:amqp results:db+mysql+pymysql://celery:**@X.X.X.X:XXXX/celeryresults
```

</p>
</details>

# Steps to Reproduce

## Required Dependencies
<!-- Please fill the required dependencies to reproduce this issue -->
* **Minimal Python Version**: N/A or Unknown
* **Minimal Celery Version**: N/A or Unknown
* **Minimal Kombu Version**: N/A or Unknown
* **Minimal Broker Version**: N/A or Unknown
* **Minimal Result Backend Version**: N/A or Unknown
* **Minimal OS and/or Kernel Version**: N/A or Unknown
* **Minimal Broker Client Version**: N/A or Unknown
* **Minimal Result Backend Client Version**: N/A or Unknown

### Python Packages
<!-- Please fill the contents of pip freeze below -->
<details>
<summary><b><code>pip freeze</code> Output:</b></summary>
<p>

```
pip freeze
amqp==2.4.2
asn1crypto==0.24.0
bcrypt==3.1.6
billiard==3.6.0.0
celery==4.3.0
cffi==1.12.2
Click==7.0
cryptography==2.6.1
Flask==1.0.2
Flask-Cors==3.0.7
itsdangerous==1.1.0
Jinja2==2.10
kombu==4.5.0
MarkupSafe==1.1.1
mysql-connector-python==8.0.15
netmiko==2.3.3
paramiko==2.4.2
protobuf==3.7.1
pyasn1==0.4.5
pycparser==2.19
PyMySQL==0.9.3
PyNaCl==1.3.0
pyserial==3.4
pytz==2018.9
PyYAML==5.1
scp==0.13.2
six==1.12.0
SQLAlchemy==1.2.14
textfsm==0.4.1
vine==1.3.0
Werkzeug==0.15.2
```

</p>
</details>

### Other Dependencies
<!--
Please provide system dependencies, configuration files
and other dependency information if applicable
-->
<details>
<p>
N/A
</p>
</details>

## Minimally Reproducible Test Case
<!--
Please provide a reproducible test case.
Refer to the Reporting Bugs section in our contribution guide.

We prefer submitting test cases in the form of a PR to our integration test suite.
If you can provide one, please mention the PR number below.
If not, please attach the most minimal code example required to reproduce the issue below.
If the test case is too large, please include a link to a gist or a repository below.
-->

<details>
<p>

```python
```

</p>
</details>

# Expected Behavior
<!-- Describe in detail what you expect to happen -->
With the following configured "result_extended" to true I expected to see more meta data in the backend results DB but I don't. I just see the standard results.

app = Celery('tasks',
             backend='db+mysql+pymysql://XXX:XXX@X.X.X.X:XXXX/celeryresults',
             broker='amqp://xxxx:xxxx@X.X.X.X/celery',
             result_extended=True)
# Actual Behavior
<!--
I only see the standard fields in the result DB and not the extended meta data as expected.
-->

Celery 4.3 result_extended doesn't push extended meta data to some backends when set to true
<!--
Please fill this template entirely and do not erase parts of it.
We reserve the right to close without a response
bug reports which are incomplete.
-->
# Checklist
<!--
To check an item on the list replace [ ] with [x].
-->

- [x] I have read the relevant section in the
  [contribution guide](http://docs.celeryproject.org/en/latest/contributing.html#other-bugs)
  on reporting bugs.
- [x] I have checked the [issues list](https://github.com/celery/celery/issues?q=is%3Aissue+label%3A%22Issue+Type%3A+Bug+Report%22+-label%3A%22Category%3A+Documentation%22)
  for similar or identical bug reports.
- [x] I have checked the [pull requests list](https://github.com/celery/celery/pulls?q=is%3Apr+label%3A%22PR+Type%3A+Bugfix%22+-label%3A%22Category%3A+Documentation%22)
  for existing proposed fixes.
- [x] I have checked the [commit log](https://github.com/celery/celery/commits/master)
  to find out if the bug was already fixed in the master branch.
- [x] I have included all related issues and possible duplicate issues
  in this issue (If there are none, check this box anyway).

## Mandatory Debugging Information

- [x] I have included the output of ``celery -A proj report`` in the issue.
    (if you are not able to do this, then at least specify the Celery
     version affected).
- [x] I have verified that the issue exists against the `master` branch of Celery.
- [x] I have included the contents of ``pip freeze`` in the issue.
- [x] I have included all the versions of all the external dependencies required
  to reproduce this bug.

## Optional Debugging Information
<!--
Try some of the below if you think they are relevant.
It will help us figure out the scope of the bug and how many users it affects.
-->
- [ ] I have tried reproducing the issue on more than one Python version
  and/or implementation.
- [ ] I have tried reproducing the issue on more than one message broker and/or
  result backend.
- [ ] I have tried reproducing the issue on more than one version of the message
  broker and/or result backend.
- [ ] I have tried reproducing the issue on more than one operating system.
- [ ] I have tried reproducing the issue on more than one workers pool.
- [ ] I have tried reproducing the issue with autoscaling, retries,
  ETA/Countdown & rate limits disabled.
- [ ] I have tried reproducing the issue after downgrading
  and/or upgrading Celery and its dependencies.

## Related Issues and Possible Duplicates
<!--
Please make sure to search and mention any related issues
or possible duplicates to this issue as requested by the checklist above.

This may or may not include issues in other repositories that the Celery project
maintains or other repositories that are dependencies of Celery.

If you don't know how to mention issues, please refer to Github's documentation
on the subject: https://help.github.com/en/articles/autolinked-references-and-urls#issues-and-pull-requests
-->

#### Related Issues

- None

#### Possible Duplicates

- None

## Environment & Settings
<!-- Include the contents of celery --version below -->
**Celery version**:
<!-- Include the output of celery -A proj report below -->
<details>
<summary><b><code>celery report</code> Output:</b></summary>
<p>

```
celery -A celeryacl report

software -> celery:4.3.0 (rhubarb) kombu:4.5.0 py:3.6.8
billiard:3.6.0.0 py-amqp:2.4.2
platform -> system:Linux arch:64bit
kernel version:4.18.0-1013-azure imp:CPython
loader -> celery.loaders.app.AppLoader
settings -> transport:amqp results:db+mysql+pymysql://celery:**@X.X.X.X:XXXX/celeryresults
```

</p>
</details>

# Steps to Reproduce

## Required Dependencies
<!-- Please fill the required dependencies to reproduce this issue -->
* **Minimal Python Version**: N/A or Unknown
* **Minimal Celery Version**: N/A or Unknown
* **Minimal Kombu Version**: N/A or Unknown
* **Minimal Broker Version**: N/A or Unknown
* **Minimal Result Backend Version**: N/A or Unknown
* **Minimal OS and/or Kernel Version**: N/A or Unknown
* **Minimal Broker Client Version**: N/A or Unknown
* **Minimal Result Backend Client Version**: N/A or Unknown

### Python Packages
<!-- Please fill the contents of pip freeze below -->
<details>
<summary><b><code>pip freeze</code> Output:</b></summary>
<p>

```
pip freeze
amqp==2.4.2
asn1crypto==0.24.0
bcrypt==3.1.6
billiard==3.6.0.0
celery==4.3.0
cffi==1.12.2
Click==7.0
cryptography==2.6.1
Flask==1.0.2
Flask-Cors==3.0.7
itsdangerous==1.1.0
Jinja2==2.10
kombu==4.5.0
MarkupSafe==1.1.1
mysql-connector-python==8.0.15
netmiko==2.3.3
paramiko==2.4.2
protobuf==3.7.1
pyasn1==0.4.5
pycparser==2.19
PyMySQL==0.9.3
PyNaCl==1.3.0
pyserial==3.4
pytz==2018.9
PyYAML==5.1
scp==0.13.2
six==1.12.0
SQLAlchemy==1.2.14
textfsm==0.4.1
vine==1.3.0
Werkzeug==0.15.2
```

</p>
</details>

### Other Dependencies
<!--
Please provide system dependencies, configuration files
and other dependency information if applicable
-->
<details>
<p>
N/A
</p>
</details>

## Minimally Reproducible Test Case
<!--
Please provide a reproducible test case.
Refer to the Reporting Bugs section in our contribution guide.

We prefer submitting test cases in the form of a PR to our integration test suite.
If you can provide one, please mention the PR number below.
If not, please attach the most minimal code example required to reproduce the issue below.
If the test case is too large, please include a link to a gist or a repository below.
-->

<details>
<p>

```python
```

</p>
</details>

# Expected Behavior
<!-- Describe in detail what you expect to happen -->
With the following configured "result_extended" to true I expected to see more meta data in the backend results DB but I don't. I just see the standard results.

app = Celery('tasks',
             backend='db+mysql+pymysql://XXX:XXX@X.X.X.X:XXXX/celeryresults',
             broker='amqp://xxxx:xxxx@X.X.X.X/celery',
             result_extended=True)
# Actual Behavior
<!--
I only see the standard fields in the result DB and not the extended meta data as expected.
-->



Discussion:
As referenced here: http://docs.celeryproject.org/en/latest/whatsnew-4.3.html#result-backends it doesn't state that any additional configuration is needed but perhaps I am missing something.
I am seeing the same issue with the MongoDB result backend. The MongoDB backend overrides [`_store_result`](https://github.com/celery/celery/blob/a616ae7c02fa137de2250f66d0c8db693e070210/celery/backends/mongodb.py#L181) and does not call the base.py `_store_result` method. The base.py [`_store_result`](https://github.com/celery/celery/blob/a616ae7c02fa137de2250f66d0c8db693e070210/celery/backends/base.py#L699) method is where the code was changed to add the extended results.

Possible solutions include adding the new result_extended code to the `_store_result` method of the MongoDB backend, or having the backend call the super method and store those values in the database. I am not sure why `_store_result` was overridden, since MongoDB can act like a key/value store. 
 
@mikolevy Thanks for the accurate analysis. We overlooked those.

This should be a very easy fix.
Any volunteers for a PR?
I don't know how much this is related, I'm using **Redis** as the broker and the result backend and I'm setting `result_extended` to `True` and everything is working fine except for the task name, when I try to access `task.name` it's always `None`, after debugging the `Context` object that is being passed to `BaseKeyValueStoreBackend.store_result` it contains the task name with the attribute `task` and not `task_name` as `BaseKeyValueStoreBackend._store_result` is trying to get from the request object:

```python
if self.app.conf.find_value_for_key('extended', 'result'):
    if request:
        request_meta = {
            'name': getattr(request, 'task_name', None),
            'args': getattr(request, 'args', None),
            'kwargs': getattr(request, 'kwargs', None),
            'worker': getattr(request, 'hostname', None),
            'retries': getattr(request, 'retries', None),
            'queue': request.delivery_info.get('routing_key')
            if hasattr(request, 'delivery_info') and
            request.delivery_info else None
        }

        meta.update(request_meta)
```
@mdawar This is a different bug, please submit a new issue about it.
> 
> 
> @mikolevy Thanks for the accurate analysis. We overlooked those.
> 
> This should be a very easy fix.
> Any volunteers for a PR?

@thedrow I am willing to give it a go unless someone who is more familiar with Celery is willing to step up.
Go for it.
@thedrow do I need contributor rights to create the branch with the fix? Also do you have some rules or guidelines on how to name the branch etc...?
Create a fork and submit a pull request from a branch.
We don't care about the branch name since it's in your fork, you can do whatever you want in your fork.

I suggest that you read our CONTRIBUTING document.
I'm not sure if I'm missing something, but this issue also seems to affect:

AMQPBackend: store_result overridden, does not implement result_extended
CassandraBackend: _store_result overridden, does not implement result_extended
DatabaseBackend: _store_result overridden, does not implement result_extended
RPCBackend: store_result overridden, does not implement result_extended

Looks like every backend that doesn't extend KeyValueStoreBackend is at fault?

Perhaps it's worth refactoring so that all the standard message 'construction' (including result_extended) is within Backend.store_result(). 

Each implementation must implement _store_result() and never, ever override store_result().

Thoughts?
You could simply create a private method which must be called in `store_result()` named `_maybe_extend_result(result)` which will add data as needed.
You are outside of my capability on this one. If someone else wants to work on a more global solution I would be all for that.
I wrote the original PR, and didn't scope it to the other backends other than KV (redis for my use case). I don't see any reason it can't be generalized to all backends; however, someone would have to go update those backends that have overridden store_result()
This should not be closed. @auvipy Can you please open it?
As referenced here: http://docs.celeryproject.org/en/latest/whatsnew-4.3.html#result-backends it doesn't state that any additional configuration is needed but perhaps I am missing something.
I am seeing the same issue with the MongoDB result backend. The MongoDB backend overrides [`_store_result`](https://github.com/celery/celery/blob/a616ae7c02fa137de2250f66d0c8db693e070210/celery/backends/mongodb.py#L181) and does not call the base.py `_store_result` method. The base.py [`_store_result`](https://github.com/celery/celery/blob/a616ae7c02fa137de2250f66d0c8db693e070210/celery/backends/base.py#L699) method is where the code was changed to add the extended results.

Possible solutions include adding the new result_extended code to the `_store_result` method of the MongoDB backend, or having the backend call the super method and store those values in the database. I am not sure why `_store_result` was overridden, since MongoDB can act like a key/value store. 
 
@mikolevy Thanks for the accurate analysis. We overlooked those.

This should be a very easy fix.
Any volunteers for a PR?
I don't know how much this is related, I'm using **Redis** as the broker and the result backend and I'm setting `result_extended` to `True` and everything is working fine except for the task name, when I try to access `task.name` it's always `None`, after debugging the `Context` object that is being passed to `BaseKeyValueStoreBackend.store_result` it contains the task name with the attribute `task` and not `task_name` as `BaseKeyValueStoreBackend._store_result` is trying to get from the request object:

```python
if self.app.conf.find_value_for_key('extended', 'result'):
    if request:
        request_meta = {
            'name': getattr(request, 'task_name', None),
            'args': getattr(request, 'args', None),
            'kwargs': getattr(request, 'kwargs', None),
            'worker': getattr(request, 'hostname', None),
            'retries': getattr(request, 'retries', None),
            'queue': request.delivery_info.get('routing_key')
            if hasattr(request, 'delivery_info') and
            request.delivery_info else None
        }

        meta.update(request_meta)
```
@mdawar This is a different bug, please submit a new issue about it.
> 
> 
> @mikolevy Thanks for the accurate analysis. We overlooked those.
> 
> This should be a very easy fix.
> Any volunteers for a PR?

@thedrow I am willing to give it a go unless someone who is more familiar with Celery is willing to step up.
Go for it.
@thedrow do I need contributor rights to create the branch with the fix? Also do you have some rules or guidelines on how to name the branch etc...?
Create a fork and submit a pull request from a branch.
We don't care about the branch name since it's in your fork, you can do whatever you want in your fork.

I suggest that you read our CONTRIBUTING document.
I'm not sure if I'm missing something, but this issue also seems to affect:

AMQPBackend: store_result overridden, does not implement result_extended
CassandraBackend: _store_result overridden, does not implement result_extended
DatabaseBackend: _store_result overridden, does not implement result_extended
RPCBackend: store_result overridden, does not implement result_extended

Looks like every backend that doesn't extend KeyValueStoreBackend is at fault?

Perhaps it's worth refactoring so that all the standard message 'construction' (including result_extended) is within Backend.store_result(). 

Each implementation must implement _store_result() and never, ever override store_result().

Thoughts?
You could simply create a private method which must be called in `store_result()` named `_maybe_extend_result(result)` which will add data as needed.
You are outside of my capability on this one. If someone else wants to work on a more global solution I would be all for that.
I wrote the original PR, and didn't scope it to the other backends other than KV (redis for my use case). I don't see any reason it can't be generalized to all backends; however, someone would have to go update those backends that have overridden store_result()
This should not be closed. @auvipy Can you please open it?

Resolving patch:
diff --git a/celery/backends/base.py b/celery/backends/base.py
--- a/celery/backends/base.py
+++ b/celery/backends/base.py
@@ -351,6 +351,54 @@ def encode_result(self, result, state):
     def is_cached(self, task_id):
         return task_id in self._cache
 
+    def _get_result_meta(self, result,
+                         state, traceback, request, format_date=True,
+                         encode=False):
+        if state in self.READY_STATES:
+            date_done = datetime.datetime.utcnow()
+            if format_date:
+                date_done = date_done.isoformat()
+        else:
+            date_done = None
+
+        meta = {
+            'status': state,
+            'result': result,
+            'traceback': traceback,
+            'children': self.current_task_children(request),
+            'date_done': date_done,
+        }
+
+        if request and getattr(request, 'group', None):
+            meta['group_id'] = request.group
+        if request and getattr(request, 'parent_id', None):
+            meta['parent_id'] = request.parent_id
+
+        if self.app.conf.find_value_for_key('extended', 'result'):
+            if request:
+                request_meta = {
+                    'name': getattr(request, 'task', None),
+                    'args': getattr(request, 'args', None),
+                    'kwargs': getattr(request, 'kwargs', None),
+                    'worker': getattr(request, 'hostname', None),
+                    'retries': getattr(request, 'retries', None),
+                    'queue': request.delivery_info.get('routing_key')
+                    if hasattr(request, 'delivery_info') and
+                    request.delivery_info else None
+                }
+
+                if encode:
+                    # args and kwargs need to be encoded properly before saving
+                    encode_needed_fields = {"args", "kwargs"}
+                    for field in encode_needed_fields:
+                        value = request_meta[field]
+                        encoded_value = self.encode(value)
+                        request_meta[field] = ensure_bytes(encoded_value)
+
+                meta.update(request_meta)
+
+        return meta
+
     def store_result(self, task_id, result, state,
                      traceback=None, request=None, **kwargs):
         """Update task state and result."""
@@ -703,40 +751,9 @@ def _forget(self, task_id):
 
     def _store_result(self, task_id, result, state,
                       traceback=None, request=None, **kwargs):
-
-        if state in self.READY_STATES:
-            date_done = datetime.datetime.utcnow().isoformat()
-        else:
-            date_done = None
-
-        meta = {
-            'status': state,
-            'result': result,
-            'traceback': traceback,
-            'children': self.current_task_children(request),
-            'task_id': bytes_to_str(task_id),
-            'date_done': date_done,
-        }
-
-        if request and getattr(request, 'group', None):
-            meta['group_id'] = request.group
-        if request and getattr(request, 'parent_id', None):
-            meta['parent_id'] = request.parent_id
-
-        if self.app.conf.find_value_for_key('extended', 'result'):
-            if request:
-                request_meta = {
-                    'name': getattr(request, 'task', None),
-                    'args': getattr(request, 'args', None),
-                    'kwargs': getattr(request, 'kwargs', None),
-                    'worker': getattr(request, 'hostname', None),
-                    'retries': getattr(request, 'retries', None),
-                    'queue': request.delivery_info.get('routing_key')
-                    if hasattr(request, 'delivery_info') and
-                    request.delivery_info else None
-                }
-
-                meta.update(request_meta)
+        meta = self._get_result_meta(result=result, state=state,
+                                     traceback=traceback, request=request)
+        meta['task_id'] = bytes_to_str(task_id)
 
         self.set(self.get_key_for_task(task_id), self.encode(meta))
         return result
diff --git a/celery/backends/database/__init__.py b/celery/backends/database/__init__.py
--- a/celery/backends/database/__init__.py
+++ b/celery/backends/database/__init__.py
@@ -5,7 +5,6 @@
 import logging
 from contextlib import contextmanager
 
-from kombu.utils.encoding import ensure_bytes
 from vine.utils import wraps
 
 from celery import states
@@ -120,6 +119,7 @@ def _store_result(self, task_id, result, state, traceback=None,
             task = task and task[0]
             if not task:
                 task = self.task_cls(task_id)
+                task.task_id = task_id
                 session.add(task)
                 session.flush()
 
@@ -128,24 +128,22 @@ def _store_result(self, task_id, result, state, traceback=None,
 
     def _update_result(self, task, result, state, traceback=None,
                        request=None):
-        task.result = result
-        task.status = state
-        task.traceback = traceback
-        if self.app.conf.find_value_for_key('extended', 'result'):
-            task.name = getattr(request, 'task', None)
-            task.args = ensure_bytes(
-                self.encode(getattr(request, 'args', None))
-            )
-            task.kwargs = ensure_bytes(
-                self.encode(getattr(request, 'kwargs', None))
-            )
-            task.worker = getattr(request, 'hostname', None)
-            task.retries = getattr(request, 'retries', None)
-            task.queue = (
-                request.delivery_info.get("routing_key")
-                if hasattr(request, "delivery_info") and request.delivery_info
-                else None
-            )
+
+        meta = self._get_result_meta(result=result, state=state,
+                                     traceback=traceback, request=request,
+                                     format_date=False, encode=True)
+
+        # Exclude the primary key id and task_id columns
+        # as we should not set it None
+        columns = [column.name for column in self.task_cls.__table__.columns
+                   if column.name not in {'id', 'task_id'}]
+
+        # Iterate through the columns name of the table
+        # to set the value from meta.
+        # If the value is not present in meta, set None
+        for column in columns:
+            value = meta.get(column)
+            setattr(task, column, value)
 
     @retry
     def _get_task_meta_for(self, task_id):
diff --git a/celery/backends/mongodb.py b/celery/backends/mongodb.py
--- a/celery/backends/mongodb.py
+++ b/celery/backends/mongodb.py
@@ -185,18 +185,10 @@ def decode(self, data):
     def _store_result(self, task_id, result, state,
                       traceback=None, request=None, **kwargs):
         """Store return value and state of an executed task."""
-        meta = {
-            '_id': task_id,
-            'status': state,
-            'result': self.encode(result),
-            'date_done': datetime.utcnow(),
-            'traceback': self.encode(traceback),
-            'children': self.encode(
-                self.current_task_children(request),
-            ),
-        }
-        if request and getattr(request, 'parent_id', None):
-            meta['parent_id'] = request.parent_id
+        meta = self._get_result_meta(result=result, state=state,
+                                     traceback=traceback, request=request)
+        # Add the _id for mongodb
+        meta['_id'] = task_id
 
         try:
             self.collection.replace_one({'_id': task_id}, meta, upsert=True)


Test patch:
diff --git a/t/unit/backends/test_base.py b/t/unit/backends/test_base.py
--- a/t/unit/backends/test_base.py
+++ b/t/unit/backends/test_base.py
@@ -7,6 +7,7 @@
 import pytest
 from case import ANY, Mock, call, patch, skip
 from kombu.serialization import prepare_accept_content
+from kombu.utils.encoding import ensure_bytes
 
 import celery
 from celery import chord, group, signature, states, uuid
@@ -104,6 +105,45 @@ def test_accept_precedence(self):
         assert list(b4.accept)[0] == 'application/x-yaml'
         assert prepare_accept_content(['yaml']) == b4.accept
 
+    def test_get_result_meta(self):
+        b1 = BaseBackend(self.app)
+        meta = b1._get_result_meta(result={'fizz': 'buzz'},
+                                   state=states.SUCCESS, traceback=None,
+                                   request=None)
+        assert meta['status'] == states.SUCCESS
+        assert meta['result'] == {'fizz': 'buzz'}
+        assert meta['traceback'] is None
+
+        self.app.conf.result_extended = True
+        args = ['a', 'b']
+        kwargs = {'foo': 'bar'}
+        task_name = 'mytask'
+
+        b2 = BaseBackend(self.app)
+        request = Context(args=args, kwargs=kwargs,
+                          task=task_name,
+                          delivery_info={'routing_key': 'celery'})
+        meta = b2._get_result_meta(result={'fizz': 'buzz'},
+                                   state=states.SUCCESS, traceback=None,
+                                   request=request, encode=False)
+        assert meta['name'] == task_name
+        assert meta['args'] == args
+        assert meta['kwargs'] == kwargs
+        assert meta['queue'] == 'celery'
+
+    def test_get_result_meta_encoded(self):
+        self.app.conf.result_extended = True
+        b1 = BaseBackend(self.app)
+        args = ['a', 'b']
+        kwargs = {'foo': 'bar'}
+
+        request = Context(args=args, kwargs=kwargs)
+        meta = b1._get_result_meta(result={'fizz': 'buzz'},
+                                   state=states.SUCCESS, traceback=None,
+                                   request=request, encode=True)
+        assert meta['args'] == ensure_bytes(b1.encode(args))
+        assert meta['kwargs'] == ensure_bytes(b1.encode(kwargs))
+
 
 class test_BaseBackend_interface:
 
diff --git a/t/unit/backends/test_database.py b/t/unit/backends/test_database.py
--- a/t/unit/backends/test_database.py
+++ b/t/unit/backends/test_database.py
@@ -246,6 +246,37 @@ def test_store_result(self, result_serializer, args, kwargs):
         assert meta['retries'] == 2
         assert meta['worker'] == "celery@worker_1"
 
+    @pytest.mark.parametrize(
+        'result_serializer, args, kwargs',
+        [
+            ('pickle', (SomeClass(1), SomeClass(2)),
+             {'foo': SomeClass(123)}),
+            ('json', ['a', 'b'], {'foo': 'bar'}),
+        ],
+        ids=['using pickle', 'using json']
+    )
+    def test_get_result_meta(self, result_serializer, args, kwargs):
+        self.app.conf.result_serializer = result_serializer
+        tb = DatabaseBackend(self.uri, app=self.app)
+
+        request = Context(args=args, kwargs=kwargs,
+                          task='mytask', retries=2,
+                          hostname='celery@worker_1',
+                          delivery_info={'routing_key': 'celery'})
+
+        meta = tb._get_result_meta(result={'fizz': 'buzz'},
+                                   state=states.SUCCESS, traceback=None,
+                                   request=request, format_date=False,
+                                   encode=True)
+
+        assert meta['result'] == {'fizz': 'buzz'}
+        assert tb.decode(meta['args']) == args
+        assert tb.decode(meta['kwargs']) == kwargs
+        assert meta['queue'] == 'celery'
+        assert meta['name'] == 'mytask'
+        assert meta['retries'] == 2
+        assert meta['worker'] == "celery@worker_1"
+
 
 @skip.unless_module('sqlalchemy')
 class test_SessionManager: