mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Allow configuration of connection_limit per postgresql database (postgresql_db) (#40345)
Fixes #40060 * Fix coding style errors * Use CONNECTION LIMIT (no underscore) * From review done by amenonsen and bcoca - Set default at None, make the change detection less confusing * Added EXAMPLE on how to apply a database specific connection limit * Added some basic tests for conn_limit applied to a database * Check that conn_limit has actually been set / updated to 200 * Add changelog fragment regarding postgresql_db conn_limit parameter
This commit is contained in:
parent
768bf5844a
commit
90c092a104
3 changed files with 77 additions and 12 deletions
2
changelogs/fragments/postgresql-db-conn-limit.yaml
Normal file
2
changelogs/fragments/postgresql-db-conn-limit.yaml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- postgresql_db - Added paramter conn_limit to limit the number of concurrent connection to a certain database
|
|
@ -83,6 +83,11 @@ options:
|
||||||
type: str
|
type: str
|
||||||
default: postgres
|
default: postgres
|
||||||
version_added: "2.5"
|
version_added: "2.5"
|
||||||
|
conn_limit:
|
||||||
|
description:
|
||||||
|
- Specifies the database connection limit.
|
||||||
|
type: str
|
||||||
|
version_added: '2.8'
|
||||||
notes:
|
notes:
|
||||||
- State C(dump) and C(restore) don't require I(psycopg2) since version 2.8.
|
- State C(dump) and C(restore) don't require I(psycopg2) since version 2.8.
|
||||||
author: "Ansible Core Team"
|
author: "Ansible Core Team"
|
||||||
|
@ -104,6 +109,12 @@ EXAMPLES = r'''
|
||||||
lc_ctype: de_DE.UTF-8
|
lc_ctype: de_DE.UTF-8
|
||||||
template: template0
|
template: template0
|
||||||
|
|
||||||
|
# Note: Default limit for the number of concurrent connections to a specific database is "-1", which means "unlimited"
|
||||||
|
- name: Create a new database with name "acme" which has a limit of 100 concurrent connections
|
||||||
|
postgresql_db:
|
||||||
|
name: acme
|
||||||
|
conn_limit: "100"
|
||||||
|
|
||||||
- name: Dump an existing database to a file
|
- name: Dump an existing database to a file
|
||||||
postgresql_db:
|
postgresql_db:
|
||||||
name: acme
|
name: acme
|
||||||
|
@ -162,6 +173,14 @@ def set_owner(cursor, db, owner):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def set_conn_limit(cursor, db, conn_limit):
|
||||||
|
query = "ALTER DATABASE %s CONNECTION LIMIT %s" % (
|
||||||
|
pg_quote_identifier(db, 'database'),
|
||||||
|
conn_limit)
|
||||||
|
cursor.execute(query)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_encoding_id(cursor, encoding):
|
def get_encoding_id(cursor, encoding):
|
||||||
query = "SELECT pg_char_to_encoding(%(encoding)s) AS encoding_id;"
|
query = "SELECT pg_char_to_encoding(%(encoding)s) AS encoding_id;"
|
||||||
cursor.execute(query, {'encoding': encoding})
|
cursor.execute(query, {'encoding': encoding})
|
||||||
|
@ -172,7 +191,7 @@ def get_db_info(cursor, db):
|
||||||
query = """
|
query = """
|
||||||
SELECT rolname AS owner,
|
SELECT rolname AS owner,
|
||||||
pg_encoding_to_char(encoding) AS encoding, encoding AS encoding_id,
|
pg_encoding_to_char(encoding) AS encoding, encoding AS encoding_id,
|
||||||
datcollate AS lc_collate, datctype AS lc_ctype
|
datcollate AS lc_collate, datctype AS lc_ctype, pg_database.datconnlimit AS conn_limit
|
||||||
FROM pg_database JOIN pg_roles ON pg_roles.oid = pg_database.datdba
|
FROM pg_database JOIN pg_roles ON pg_roles.oid = pg_database.datdba
|
||||||
WHERE datname = %(db)s
|
WHERE datname = %(db)s
|
||||||
"""
|
"""
|
||||||
|
@ -195,8 +214,8 @@ def db_delete(cursor, db):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def db_create(cursor, db, owner, template, encoding, lc_collate, lc_ctype):
|
def db_create(cursor, db, owner, template, encoding, lc_collate, lc_ctype, conn_limit):
|
||||||
params = dict(enc=encoding, collate=lc_collate, ctype=lc_ctype)
|
params = dict(enc=encoding, collate=lc_collate, ctype=lc_ctype, conn_limit=conn_limit)
|
||||||
if not db_exists(cursor, db):
|
if not db_exists(cursor, db):
|
||||||
query_fragments = ['CREATE DATABASE %s' % pg_quote_identifier(db, 'database')]
|
query_fragments = ['CREATE DATABASE %s' % pg_quote_identifier(db, 'database')]
|
||||||
if owner:
|
if owner:
|
||||||
|
@ -209,6 +228,8 @@ def db_create(cursor, db, owner, template, encoding, lc_collate, lc_ctype):
|
||||||
query_fragments.append('LC_COLLATE %(collate)s')
|
query_fragments.append('LC_COLLATE %(collate)s')
|
||||||
if lc_ctype:
|
if lc_ctype:
|
||||||
query_fragments.append('LC_CTYPE %(ctype)s')
|
query_fragments.append('LC_CTYPE %(ctype)s')
|
||||||
|
if conn_limit:
|
||||||
|
query_fragments.append("CONNECTION LIMIT %(conn_limit)s" % {"conn_limit": conn_limit})
|
||||||
query = ' '.join(query_fragments)
|
query = ' '.join(query_fragments)
|
||||||
cursor.execute(query, params)
|
cursor.execute(query, params)
|
||||||
return True
|
return True
|
||||||
|
@ -230,13 +251,19 @@ def db_create(cursor, db, owner, template, encoding, lc_collate, lc_ctype):
|
||||||
'Changing LC_CTYPE is not supported.'
|
'Changing LC_CTYPE is not supported.'
|
||||||
'Current LC_CTYPE: %s' % db_info['lc_ctype']
|
'Current LC_CTYPE: %s' % db_info['lc_ctype']
|
||||||
)
|
)
|
||||||
elif owner and owner != db_info['owner']:
|
|
||||||
return set_owner(cursor, db, owner)
|
|
||||||
else:
|
else:
|
||||||
return False
|
changed = False
|
||||||
|
|
||||||
|
if owner and owner != db_info['owner']:
|
||||||
|
changed = set_owner(cursor, db, owner)
|
||||||
|
|
||||||
|
if conn_limit and conn_limit != str(db_info['conn_limit']):
|
||||||
|
changed = set_conn_limit(cursor, db, conn_limit)
|
||||||
|
|
||||||
|
return changed
|
||||||
|
|
||||||
|
|
||||||
def db_matches(cursor, db, owner, template, encoding, lc_collate, lc_ctype):
|
def db_matches(cursor, db, owner, template, encoding, lc_collate, lc_ctype, conn_limit):
|
||||||
if not db_exists(cursor, db):
|
if not db_exists(cursor, db):
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
|
@ -250,6 +277,8 @@ def db_matches(cursor, db, owner, template, encoding, lc_collate, lc_ctype):
|
||||||
return False
|
return False
|
||||||
elif owner and owner != db_info['owner']:
|
elif owner and owner != db_info['owner']:
|
||||||
return False
|
return False
|
||||||
|
elif conn_limit and conn_limit != str(db_info['conn_limit']):
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -397,6 +426,7 @@ def main():
|
||||||
target_opts=dict(type='str', default=''),
|
target_opts=dict(type='str', default=''),
|
||||||
maintenance_db=dict(type='str', default="postgres"),
|
maintenance_db=dict(type='str', default="postgres"),
|
||||||
session_role=dict(type='str'),
|
session_role=dict(type='str'),
|
||||||
|
conn_limit=dict(type='str', default='')
|
||||||
)
|
)
|
||||||
|
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
|
@ -416,6 +446,7 @@ def main():
|
||||||
changed = False
|
changed = False
|
||||||
maintenance_db = module.params['maintenance_db']
|
maintenance_db = module.params['maintenance_db']
|
||||||
session_role = module.params["session_role"]
|
session_role = module.params["session_role"]
|
||||||
|
conn_limit = module.params['conn_limit']
|
||||||
|
|
||||||
raw_connection = state in ("dump", "restore")
|
raw_connection = state in ("dump", "restore")
|
||||||
|
|
||||||
|
@ -481,7 +512,7 @@ def main():
|
||||||
if state == "absent":
|
if state == "absent":
|
||||||
changed = db_exists(cursor, db)
|
changed = db_exists(cursor, db)
|
||||||
elif state == "present":
|
elif state == "present":
|
||||||
changed = not db_matches(cursor, db, owner, template, encoding, lc_collate, lc_ctype)
|
changed = not db_matches(cursor, db, owner, template, encoding, lc_collate, lc_ctype, conn_limit)
|
||||||
module.exit_json(changed=changed, db=db)
|
module.exit_json(changed=changed, db=db)
|
||||||
|
|
||||||
if state == "absent":
|
if state == "absent":
|
||||||
|
@ -492,7 +523,7 @@ def main():
|
||||||
|
|
||||||
elif state == "present":
|
elif state == "present":
|
||||||
try:
|
try:
|
||||||
changed = db_create(cursor, db, owner, template, encoding, lc_collate, lc_ctype)
|
changed = db_create(cursor, db, owner, template, encoding, lc_collate, lc_ctype, conn_limit)
|
||||||
except SQLParseError as e:
|
except SQLParseError as e:
|
||||||
module.fail_json(msg=to_native(e), exception=traceback.format_exc())
|
module.fail_json(msg=to_native(e), exception=traceback.format_exc())
|
||||||
|
|
||||||
|
|
|
@ -101,14 +101,15 @@
|
||||||
# that: "result.stdout_lines[-1] == '(0 rows)'"
|
# that: "result.stdout_lines[-1] == '(0 rows)'"
|
||||||
|
|
||||||
#
|
#
|
||||||
# Test encoding, collate, ctype, template options
|
# Test conn_limit, encoding, collate, ctype, template options
|
||||||
#
|
#
|
||||||
- name: Create a DB with encoding, collate, ctype, and template options
|
- name: Create a DB with conn_limit, encoding, collate, ctype, and template options
|
||||||
become_user: "{{ pg_user }}"
|
become_user: "{{ pg_user }}"
|
||||||
become: True
|
become: True
|
||||||
postgresql_db:
|
postgresql_db:
|
||||||
name: '{{ db_name }}'
|
name: '{{ db_name }}'
|
||||||
state: 'present'
|
state: 'present'
|
||||||
|
conn_limit: '100'
|
||||||
encoding: 'LATIN1'
|
encoding: 'LATIN1'
|
||||||
lc_collate: 'pt_BR{{ locale_latin_suffix }}'
|
lc_collate: 'pt_BR{{ locale_latin_suffix }}'
|
||||||
lc_ctype: 'es_ES{{ locale_latin_suffix }}'
|
lc_ctype: 'es_ES{{ locale_latin_suffix }}'
|
||||||
|
@ -118,7 +119,7 @@
|
||||||
- name: Check that the DB has all of our options
|
- name: Check that the DB has all of our options
|
||||||
become_user: "{{ pg_user }}"
|
become_user: "{{ pg_user }}"
|
||||||
become: True
|
become: True
|
||||||
shell: echo "select datname, pg_encoding_to_char(encoding), datcollate, datctype from pg_database where datname = '{{ db_name }}';" | psql -d postgres
|
shell: echo "select datname, datconnlimit, pg_encoding_to_char(encoding), datcollate, datctype from pg_database where datname = '{{ db_name }}';" | psql -d postgres
|
||||||
register: result
|
register: result
|
||||||
|
|
||||||
- assert:
|
- assert:
|
||||||
|
@ -129,6 +130,7 @@
|
||||||
- "'es_ES' in result.stdout_lines[-2]"
|
- "'es_ES' in result.stdout_lines[-2]"
|
||||||
- "'UTF8' not in result.stdout_lines[-2]"
|
- "'UTF8' not in result.stdout_lines[-2]"
|
||||||
- "'en_US' not in result.stdout_lines[-2]"
|
- "'en_US' not in result.stdout_lines[-2]"
|
||||||
|
- "'100' in result.stdout_lines[-2]"
|
||||||
|
|
||||||
- name: Check that running db cration with options a second time does nothing
|
- name: Check that running db cration with options a second time does nothing
|
||||||
become_user: "{{ pg_user }}"
|
become_user: "{{ pg_user }}"
|
||||||
|
@ -136,6 +138,7 @@
|
||||||
postgresql_db:
|
postgresql_db:
|
||||||
name: '{{ db_name }}'
|
name: '{{ db_name }}'
|
||||||
state: 'present'
|
state: 'present'
|
||||||
|
conn_limit: '100'
|
||||||
encoding: 'LATIN1'
|
encoding: 'LATIN1'
|
||||||
lc_collate: 'pt_BR{{ locale_latin_suffix }}'
|
lc_collate: 'pt_BR{{ locale_latin_suffix }}'
|
||||||
lc_ctype: 'es_ES{{ locale_latin_suffix }}'
|
lc_ctype: 'es_ES{{ locale_latin_suffix }}'
|
||||||
|
@ -166,6 +169,35 @@
|
||||||
that:
|
that:
|
||||||
- 'result.failed == True'
|
- 'result.failed == True'
|
||||||
|
|
||||||
|
- name: Check that changing the conn_limit actually works
|
||||||
|
become_user: "{{ pg_user }}"
|
||||||
|
become: True
|
||||||
|
postgresql_db:
|
||||||
|
name: '{{ db_name }}'
|
||||||
|
state: 'present'
|
||||||
|
conn_limit: '200'
|
||||||
|
encoding: 'LATIN1'
|
||||||
|
lc_collate: 'pt_BR{{ locale_latin_suffix }}'
|
||||||
|
lc_ctype: 'es_ES{{ locale_latin_suffix }}'
|
||||||
|
template: 'template0'
|
||||||
|
login_user: "{{ pg_user }}"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- 'result.changed == True'
|
||||||
|
|
||||||
|
- name: Check that conn_limit has actually been set / updated to 200
|
||||||
|
become_user: "{{ pg_user }}"
|
||||||
|
become: True
|
||||||
|
shell: echo "SELECT datconnlimit AS conn_limit FROM pg_database WHERE datname = '{{ db_name }}';" | psql -d postgres
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- "result.stdout_lines[-1] == '(1 row)'"
|
||||||
|
- "'200' == '{{ result.stdout_lines[-2] | trim }}'"
|
||||||
|
|
||||||
- name: Cleanup test DB
|
- name: Cleanup test DB
|
||||||
become_user: "{{ pg_user }}"
|
become_user: "{{ pg_user }}"
|
||||||
become: True
|
become: True
|
||||||
|
|
Loading…
Reference in a new issue