Bug 51952 - Write /var/cache/univention-portal/groups.json atomic to avoid invalid json
Write /var/cache/univention-portal/groups.json atomic to avoid invalid json
Status: RESOLVED WONTFIX
Product: UCS
Classification: Unclassified
Component: Portal
UCS 4.4
Other Linux
: P5 normal (vote)
: ---
Assigned To: UMC maintainers
UMC maintainers
https://help.univention.com/t/problem...
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2020-09-07 16:08 CEST by Arvid Requate
Modified: 2023-01-18 17:21 CET (History)
5 users (show)

See Also:
What kind of report is it?: Bug Report
What type of bug is this?: 5: Major Usability: Impairs usability in key scenarios
Who will be affected by this bug?: 2: Will only affect a few installed domains
How will those affected feel about the bug?: 2: A Pain – users won’t like this once they notice it
User Pain: 0.114
Enterprise Customer affected?:
School Customer affected?: Yes
ISV affected?:
Waiting Support:
Flags outvoted (downgraded) after PO Review:
Ticket number: 2020090721000162, 2022102121000414
Bug group (optional):
Max CVSS v3 score:


Attachments
rebuild-portal-groups (1.18 KB, text/x-python)
2020-09-07 16:22 CEST, Arvid Requate
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Arvid Requate univentionstaff 2020-09-07 16:08:30 CEST
In a customer environment the /var/cache/univention-portal/groups.json file did not contain valid JSON, starting with '{"cn=' and ending with '"someusername",'

I guess this was because support had to kill the listener at some point. Stsarting the listener in this state results in a flood of tracebacks:
==========================================================================
07.09.20 15:52:13.304  LISTENER    ( ERROR   ) : portal_groups: dn='cn=foo123,cn=klassen,cn=schueler,cn=groups,ou=bar,dc=domain,dc=local' command='m'
    old={'cn': ...}
    new={'cn': ...}
Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/univention/listener/api_adapter.py", line 147, in _handler
    self._module_handler.modify(dn, old, new, self._saved_old_dn if self._rename else None)
  File "/usr/lib/univention-directory-listener/system/portal_groups.py", line 60, in modify
    groups = self._load()
  File "/usr/lib/univention-directory-listener/system/portal_groups.py", line 82, in _load
    return json.load(fd)
  File "/usr/lib/python2.7/json/__init__.py", line 291, in load
    **kw)
  File "/usr/lib/python2.7/json/__init__.py", line 339, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python2.7/json/decoder.py", line 364, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python2.7/json/decoder.py", line 380, in raw_decode
    obj, end = self.scan_once(s, idx)
ValueError: Expecting object: line 1 column 2842624 (char 2842623)
Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/univention/listener/api_adapter.py", line 155, in _handler
    self._module_handler.error_handler(dn, old, new, command, exc_type, exc_value, exc_traceback)
  File "/usr/lib/python2.7/dist-packages/univention/listener/handler.py", line 243, in error_handler
    reraise(exc_type, exc_value, exc_traceback)
  File "/usr/lib/python2.7/dist-packages/univention/listener/api_adapter.py", line 147, in _handler
    self._module_handler.modify(dn, old, new, self._saved_old_dn if self._rename else None)
  File "/usr/lib/univention-directory-listener/system/portal_groups.py", line 60, in modify
    groups = self._load()
  File "/usr/lib/univention-directory-listener/system/portal_groups.py", line 82, in _load
    return json.load(fd)
  File "/usr/lib/python2.7/json/__init__.py", line 291, in load
    **kw)
  File "/usr/lib/python2.7/json/__init__.py", line 339, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python2.7/json/decoder.py", line 364, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python2.7/json/decoder.py", line 380, in raw_decode
    obj, end = self.scan_once(s, idx)
ValueError: Expecting object: line 1 column 2842624 (char 2842623)
07.09.20 15:37:11.248  LISTENER    ( WARN    ) : handler: portal_groups (failed)
==========================================================================

We should find a more robust (and performant) implementation for this. python-lmdb would be an option.
Comment 1 Arvid Requate univentionstaff 2020-09-07 16:22:00 CEST
Created attachment 10471 [details]
rebuild-portal-groups

Dirk provided a script to reconstruct the file:

systemctl stop univention-directory-listener
./rebuild-portal-groups > groups.json
## Inspect file and then
chgrp nogroup groups.json
mv groups.json /var/cache/univention-portal/groups.json
systemctl start univention-directory-listener
Comment 2 Jürn Brodersen univentionstaff 2020-12-18 12:50:39 CET
This is also a problem in huge environments (customers groups.json > 6Mb)

In a customers environment it seems "/var/cache/univention-portal/groups.json" was written by the "portal_groups.py" listener while the portal tried to read the "groups.json". This resulted in an empty portal page. While this case was self healing (you can wait a moment and reload), it is still annoying and created a support case.

We should adjust the listener to write the groups cache atomic.


Traceback from the portal server:
'''
Traceback (most recent call last):                                              
  File "/usr/lib/python2.7/dist-packages/tornado/web.py", line 1467, in _execute
    result = method(*self.path_args, **self.path_kwargs)                        
  File "/usr/bin/univention-portal-server", line 468, in get                    
    portal_content = answer['portal'] = self._get_portal(username, admin_mode)  
  File "/usr/bin/univention-portal-server", line 373, in _get_portal            
    groups = cache.get('groups')                                                
  File "/usr/bin/univention-portal-server", line 158, in get                    
    return self._get_groups() or {}                                             
  File "/usr/bin/univention-portal-server", line 133, in _get_groups            
    self._groups = json.load(fd)                                                
  File "/usr/lib/python2.7/json/__init__.py", line 291, in load                 
    **kw)                                                                       
  File "/usr/lib/python2.7/json/__init__.py", line 339, in loads                
    return _default_decoder.decode(s)                                           
  File "/usr/lib/python2.7/json/decoder.py", line 364, in decode                
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())                           
  File "/usr/lib/python2.7/json/decoder.py", line 380, in raw_decode            
    obj, end = self.scan_once(s, idx)                                           
ValueError: Unterminated string starting at: line 1 column 5468156 (char 5468155)
'''
Comment 4 Dirk Wiesenthal univentionstaff 2023-01-18 17:21:12 CET
This is a UCS 4.4 issue. In UCS 5.0, the JSON generation has been reworked and is atomic.