рд▓реЗрдЦ CI рдореЗрдВ рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЗ рд╕рд╛рде рдмрд╛рддрдЪреАрдд рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╣реИред рдореИрдВрдиреЗ docker рдФрд░ testcontainers рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдХрдИ рд╕рдорд╛рдзрд╛рди рджреЗрдЦреЗ, рд▓реЗрдХрд┐рди рдореЗрд░рд╛ рдЕрдкрдирд╛ рд╣реИ рдФрд░ рдореИрдВ рдЗрд╕реЗ рд╕рд╛рдЭрд╛ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВред
рдореЗрд░рд╛ рдкрд┐рдЫрд▓рд╛ рдЬрд╛рд╡рд╛ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдПрдХ рдбреЗрдЯрд╛рдмреЗрд╕ рд╕реЗ рдирд┐рдХрдЯрддрд╛ рд╕реЗ рдЬреБрдбрд╝рд╛ рдерд╛ред рд░рд┐рдЯреНрд░реАрдЯ, рдорд▓реНрдЯреАрдереНрд░реЗрдбрд┐рдВрдЧ рдФрд░ рд▓реЙрдХ рд╕реИрдВрдкрд▓ рдХреЗ рд╕рд╛рде рд▓рдВрдмреА рдкреНрд░реЛрд╕реЗрд╕рд┐рдВрдЧред рдЯрд╛рд╕реНрдХ рдХреЗ рд▓рд┐рдП рдпрд╣ рдЯреНрд░рд┐рдХреА SQL рдХреНрд╡реЗрд░реА рдХреЗ рдПрдХ рдЬреЛрдбрд╝реЗ рдХреЛ рд╕рд╣реА рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рдерд╛ред рдореБрдЭреЗ рдХрд┐рд╕реА рддрд░рд╣ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЗ рд╕рд╛рде рдЕрдкрдиреЗ рдХреЛрдб рдХреЛ рдХрд╡рд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рд▓реЗрдХрд┐рди рдЗрд╕рд╕реЗ рдкрд╣рд▓реЗ рд╕рднреА рдПрд╕рдХреНрдпреВрдПрд▓ рдХреЛ рдЖрджрд┐рдо рдкреНрд░рд╢реНрдиреЛрдВ рдореЗрдВ рдШрдЯрд╛ рджрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ рдФрд░ рдЗрд╕реЗ рдореЗрдореЛрд░реА рдореЗрдВ рдПрдЪ 2 рдмреЗрд╕ рдкрд░ рдЪрд▓рд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рдерд╛ред рдФрд░ рдпрд╣рд╛рдБ рдХрдЯреНрдЯрд░ рдкрд░ред
рдореИрдВ рдЕрдкрдиреЗ рд╣рд╛рдереЛрдВ рд╕реЗ рд╕рд░рд▓ рдПрд╕рдХреНрдпреВрдПрд▓ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░ рд╕рдХрддрд╛ рдерд╛ рдФрд░ рдЖрддреНрдо-рдкрд░реАрдХреНрд╖рдг рдкрд░ рдХрд╛рдпрд░рддрд╛рдкреВрд░реНрд╡рдХ рд╣рдереМрдбрд╝рд╛ рдорд╛рд░ рд╕рдХрддрд╛ рдерд╛, рдЕрдкрдиреЗ рдЖрдк рдХреЛ рдЙрдЪрд┐рдд рдард╣рд░рд╛рддреЗ рд╣реБрдП "рдореИрдВ рдХрд┐рд╕реА рддрд░рд╣ рдХрд╛ рдмреИрдЧреЛрдбреЗрд▓ рдирд╣реАрдВ рд╣реВрдВ, рдореИрдВ рд╕рд░рд▓ рдХреЛрдб рдореЗрдВ рдЧрд▓рддрд┐рдпрд╛рдВ рдХрд░реВрдВрдЧрд╛ред" рджрд░рдЕрд╕рд▓, рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреА рдЙрдкрд▓рдмреНрдзрддрд╛ рдХреЗ рдХрд╛рд░рдг рддреНрд░реБрдЯрд┐рдпрд╛рдВ рдХрдо рджрд┐рдЦрд╛рдИ рджреЗрддреА рд╣реИрдВред рдкрд░реАрдХреНрд╖рдХреЛрдВ рдХреЛ рдЬрд┐рдореНрдореЗрджрд╛рд░реА рд╕реМрдВрдкрдирд╛ рд╕рдВрднрд╡ рдерд╛ - рдЕрдЧрд░ рдореИрдВрдиреЗ рдХрд╣реАрдВ рдЧрд▓рддреА рдХреА, рддреЛ рд╡реЗ рдЗрд╕реЗ рдвреВрдВрдв рд▓реЗрдВрдЧреЗред

рдпреВрдирд┐рдЯ рдЯреЗрд╕реНрдЯрд┐рдВрдЧ рдХреА рд╡рд┐рдЪрд╛рд░рдзрд╛рд░рд╛ рдХреЗ рдЕрдиреБрд╕рд╛рд░, рдХреЗрд╡рд▓ рд╡реНрдпрдХреНрддрд┐рдЧрдд рдореЙрдбреНрдпреВрд▓ рдХреЗ рдкрд░реАрдХреНрд╖рдг рдХреЗ рд▓рд┐рдП рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ, рдФрд░ рдпрджрд┐ рдореЙрдбреНрдпреВрд▓ рдмрд╛рд╣рд░ рд╕реЗ рдХреБрдЫ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИ, рддреЛ рдпрд╣ рдПрдХ рд╕реНрдЯрдм рдХреЗ рд╕рд╛рде рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред рд╡реНрдпрд╡рд╣рд╛рд░ рдореЗрдВ, рдЬрдм рд╕реНрдЯрдм рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдирд╛ рдмрд╣реБрдд рдореБрд╢реНрдХрд┐рд▓ рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ, рддреЛ рдореЙрдбреНрдпреВрд▓ рдХреЛ рдХреЗрд╡рд▓ рдЕрдирджреЗрдЦрд╛ рдХрд░ рджрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЧрддрд┐ рдФрд░ рдкреНрд░рддрд┐рд░реВрдкрдХрддрд╛ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рдЕрдзрд┐рдХ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ рдХрд┐ рдХреЛрдб рдХреЛ рди рдЫреЛрдбрд╝рд╛ рдЬрд╛рдП, рднрд▓реЗ рд╣реА рд╡рд╣ рдХрд╡рд░реЗрдЬ рдореЗрдЯреНрд░рд┐рдХреНрд╕ рдореЗрдВ рджрд┐рдЦрд╛рдИ рди рджреЗред рдЗрд╕рд▓рд┐рдП, рдореЙрдбреНрдпреВрд▓ рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдХреЗ рд╕рд╛рде рдПрдХ рдЕрд▓рдЧ рд╡рд░реНрдЧ рдирд╣реАрдВ рдорд╛рдирд╛ рдЬрд╛рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдПрдХ рдЬреБрдбрд╝рд╛ рд╣реБрдЖ рд╕рдореВрд╣ рд╣реИред рдЗрд╕ рддрд░рд╣ рдХреЗ рдмрдпрд╛рдиреЛрдВ рдХреЗ рд╕рд╛рде рдореБрдЦреНрдп рдмрд╛рдд рдпрд╣ рд╣реИ рдХрд┐ рдХреНрд▓рд╕реНрдЯрд░ рд╕реНрддрд░ рддрдХ рдЗрдХрд╛рдИ рдХреА рдЕрд╡рдзрд╛рд░рдгрд╛ рдХреЛ рдкреНрд░рд╛рдкреНрдд рдирд╣реАрдВ рдХрд░рдирд╛ рд╣реИред
рд╕реНрдерд╛рдиреАрдп рдкрд░реАрдХреНрд╖рдг рдХреЗ рд▓рд┐рдП, рдкреНрд░рддреНрдпреЗрдХ рдбреЗрд╡рд▓рдкрд░ рдХреА рдЕрдкрдиреА рдпреЛрдЬрдирд╛ рд╣реЛрддреА рд╣реИ, рд▓реЗрдХрд┐рди рдкрд░реАрдХреНрд╖рдг рдЬреЗрдирдХрд┐рдиреНрд╕ рдкрд░ рдЪрд▓рд╛рдП рдЬрд╛рддреЗ рд╣реИрдВ рдФрд░ рд╡рд╣рд╛рдВ рдЕрднреА рддрдХ рдбреЗрдЯрд╛рдмреЗрд╕ рд╕реЗ рдХреЛрдИ рд╕рдВрдмрдВрдз рдирд╣реАрдВ рд╣реИред CI рдХреЛ рдПрдХ рдЕрд▓рдЧ рд╕рд░реНрдХрд┐рдЯ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ, рдпрд╣ рд╕реНрдкрд╖реНрдЯ рдкреНрд░рддреАрдд рд╣реЛрддрд╛ рд╣реИред рд▓реЗрдХрд┐рди рдПрдХ рдЦрд╛рд▓реА рдЖрд░реЗрдЦ рдкрд░, рдкрд░реАрдХреНрд╖рдг рдЪрд▓рд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдпрд╣ рдмрд╣реБрдд рд╕рд╣реА рдирд╣реАрдВ рд╣реИ, рдкреНрд░рддреНрдпреЗрдХ рдкрд░реАрдХреНрд╖рдг рдореЗрдВ рдПрдХ рдбреЗрдЯрд╛рдмреЗрд╕ рд╕рдВрд░рдЪрдирд╛ рдмрдирд╛рдирд╛ рд╕рдордп рд▓реЗрдиреЗ рд╡рд╛рд▓реА рдФрд░ рдкрд░реАрдХреНрд╖рдг рдореЗрдВ рд╕рдВрд░рдЪрдирд╛ рдФрд░ рд▓рдбрд╝рд╛рдИ рдХреЗ рдмреАрдЪ рдПрдХ рд╡рд┐рд╕рдВрдЧрддрд┐ рд╕реЗ рднрд░рд╛ рд╣реИред рдкреВрд░реНрд╡-рддреИрдпрд╛рд░ рдЖрдзрд╛рд░ рдкрд░ рдЪрд▓рд╛рдПрдВ - рдореБрдЭреЗ рд╢рд╛рдЦрд╛рдУрдВ рдХреЗ рд╕рд╛рде рд╕рдорд╕реНрдпрд╛рдУрдВ рдХрд╛ рдПрдХ рдЧреБрдЪреНрдЫрд╛ рдорд┐рд▓рддрд╛ рд╣реИред рдЖрдк рд▓рд┐рдмрд╛рд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╕рднреА рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЛ рдЪрд▓рд╛рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЛ рддреИрдпрд╛рд░ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдкрд╣рд▓реЗ рд╕рдмрдХреБрдЫ рд╢реВрдиреНрдп рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдФрд░ рдлрд┐рд░ рдирд╡реАрдирддрдо рд╕рдВрд╕реНрдХрд░рдг рдореЗрдВ рдЕрдкрдбреЗрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
рд░реЛрд▓рдмреИрдХ рдХреЛ рдЕрдХреНрд╕рд░ рдЕрдВрддрд┐рдо рд░реВрдк рджреЗрдирд╛ рднреВрд▓ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ рдЖрдкрдХреЛ рдкрд░реАрдХреНрд╖рдг рд╡рд╛рддрд╛рд╡рд░рдг рдкрд░ рдЕрдкрдиреЗ рд╣рд╛рдереЛрдВ рд╕реЗ рдард┐рдХрд╛рдиреЛрдВ рдХреЛ рд╕рд╛рдл рдХрд░рдирд╛ рдкрдбрд╝рддрд╛ рд╣реИред рдпрд╣ рдФрд░ рдЙрд╕реЗ рдкрд░реАрдХреНрд╖рдг! рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдЗрд╕ рдкреНрд░рдХрд╛рд░ рд╣реИ:
- рд░реВрдЯ рдХреЗ рддрд╣рдд рд╕рдм рдХреБрдЫ рд╣рдЯрд╛ рджреЗрдВ (рдкреНрд░рдпреЛрдЧ рдХреА рд╢реБрджреНрдзрддрд╛ рдХреЗ рд▓рд┐рдП)
- рдирд╡реАрдирддрдо рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рд▓рд┐рдП рдЕрджреНрдпрддрди
- рд░реЛрд▓рдмреИрдХ 1-2 рд╕рдВрд╕реНрдХрд░рдг рд╡рд╛рдкрд╕ рдХрд░реЗрдВ
- рдирд╡реАрдирддрдо рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рд▓рд┐рдП рдЕрджреНрдпрддрди (рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЛ рдирдП рдбреЗрдЯрд╛рдмреЗрд╕ рд╕рдВрд░рдЪрдирд╛ рдкрд░ рд╕рдВрдЪрд╛рд▓рд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рд╕рд╛рде рд╣реА рдпрд╣ рдЬрд╛рдВрдЪрдирд╛ рдХрд┐ рд░реЛрд▓рдмреИрдХ рдХреБрдЫ рднреА рд╣рдЯрд╛рдирд╛ рдирд╣реАрдВ рднреВрд▓рддрд╛, рдЬреЛ рдЕрджреНрдпрддрди рдХреЛ рдлрд┐рд░ рд╕реЗ рдЪрд╛рд▓реВ рдХрд░рдиреЗ рд╕реЗ рд░реЛрдХреЗрдЧрд╛)
рдбреЗрд╡рд▓рдкрд░реНрд╕ рдХреЗ рд╕рд╣рдпреЛрдЧреА рдкреНрд░рддреНрдпреЗрдХ рдкрд░реАрдХреНрд╖рдг рдХреА рд╢реБрд░реБрдЖрдд рдореЗрдВ рд░реЛрд▓рдмреИрдХ рдкрд░реАрдХреНрд╖рдг рдирд╣реАрдВ рдЪрд▓рд╛рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВред рд╣рдо рд╕реНрд╡рд┐рдЪ рдмрдирд╛рддреЗ рд╣реИрдВред
project.ext.doRollbackTest = { Boolean.parseBoolean(localConfig['test.rollback.enabled'] as String) }
рдЬрдмрдХрд┐ рдмрд┐рд▓реНрд▓рд┐рдпреЛрдВ рдкрд░ рдкреНрд░рд╢рд┐рдХреНрд╖рдг рд╣реИ, рд╕рдм рдХреБрдЫ рдареАрдХ рд╣реИред рд▓реЗрдХрд┐рди рдПрдХ рдЧрддрд┐рд╢реАрд▓ рд░реВрдк рд╕реЗ рд╡рд┐рдХрд╛рд╕рд╢реАрд▓ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдЕрдкрдирд╛ рд╕рдорд╛рдпреЛрдЬрди рдХрд░рддреА рд╣реИ - 2 рдкреБрд▓-рдХреНрд╡реИрд╕реНрдЯ, рдПрдХ рд╕рд╛рде рдЕрд╕реЗрдВрдмрд▓реАред рдПрдХ рдЙрджрд╛рд╣рд░рдг рдХреБрдЫ рдкрд░реАрдХреНрд╖рдг рдХрд░ рд░рд╣рд╛ рд╣реИ, рдФрд░ рджреВрд╕рд░рд╛ рдЕрдкрдиреЗ рдкреИрд░реЛрдВ рдХреЗ рдиреАрдЪреЗ рд╕реЗ рдЖрдзрд╛рд░ рдЦрдЯрдЦрдЯрд╛ рд░рд╣рд╛ рд╣реИред рдпрд╣ рд╕рдорд╛рдирд╛рдВрддрд░ рд╡рд┐рдзрд╛рдирд╕рднрд╛рдУрдВ рдкрд░ рдПрдХ рд╕рд░рд▓ рдкреНрд░рддрд┐рдмрдВрдз рджреНрд╡рд╛рд░рд╛ рд╣рд▓ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред

рдФрд░ рдлрд┐рд░ рд╕реЗ рдЧрд┐рд░рд╛рд╡рдЯ - рдореИрдВрдиреЗ рдЬреЗрдирдХрд┐рдВрд╕ рдЦрд╛рддреЗ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдкрд░реАрдХреНрд╖рдг рдЪрд▓рд╛рдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛, рдХреНрдпреЛрдВрдХрд┐ рд╡реНрдпрдХреНрддрд┐рдЧрдд рдПрдХ рдкрд░ рд╕рдм рдХреБрдЫ рдареАрдХ рд╣реИ, рдФрд░ рдЕрдиреБрд░реЛрдзреЛрдВ рдХрд╛ рдкреВрд▓ рдЕрд╕реНрдкрд╖реНрдЯ рдХрд╛рд░рдгреЛрдВ рд╕реЗ рдЧрд┐рд░рддрд╛ рд╣реИред рд╣рдо рдЗрд╕ рдмрд╛рдд рдХреЛ рдпрд╛рдж рдХрд░рддреЗ рд╣реИрдВ рдХрд┐ рджреЗрд╡рдУрдк рдПрдХ рд╕рдВрд╕реНрдХреГрддрд┐ рд╣реИ рдФрд░ рдирд┐рдЬреА рдЙрджреНрджреЗрд╢реНрдпреЛрдВ рдХреЗ рд▓рд┐рдП рддрдХрдиреАрдХреА рдЦрд╛рддреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдЕрд╕реНрд╡реАрдХрд╛рд░реНрдп рд╣реИред

рд╕рдВрдЪрд┐рдд 10 рдЕрдиреБрд░реЛрдзреЛрдВ рдХрд╛ рдкреВрд▓ред рд╕рднреА рдПрдХрддреНрд░, рдкреБрдирд░реНрд╡рд┐рддрд░рд┐рдд, рдЖрдк рд╡рд┐рд▓рдп рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдкрд╣рд▓рд╛ рдЪрд▓рд╛ рдЧрдпрд╛, рдореБрдЦреНрдп рд╢рд╛рдЦрд╛ рдмрджрд▓ рдЧрдИ - рдмрд╛рдХреА рдПрдХ рд╕рд╛рде рдкреБрдирд░реНрдирд┐рд░реНрдорд╛рдг рдХреЗ рд▓рд┐рдП рдХрддрд╛рд░ рдореЗрдВ рдЦрдбрд╝реЗ рд╣реИрдВред рдЖрдк рдкреНрд░рдЧрддрд┐ рдХреЗ рд░реВрдк рдореЗрдВ рд╡рд┐рд▓рдп рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдкреНрд░рд╛рдердорд┐рдХрддрд╛рдПрдВ рд╣реИрдВред рдХреЛрдб рдореЗрдВ рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЗ рдХрд╛рд░рдг рдХрдо рдЬрд░реВрд░реА, рдЕрдзрд┐рдХ рдЬрд░реВрд░реА рдкреБрд▓рдХреНрд░реЗрдХ рднреА рд▓рдЯрдХ рд░рд╣реЗ рд╣реИрдВред рд╕рд╛рдорд╛рдиреНрдп рддреМрд░ рдкрд░, рд╡рд╛рдкрд╕ рд╕рдорд╛рдирд╛рдВрддрд░ред
рд╡рд┐рдзрд╛рдирд╕рднрд╛ рд╕рд░рд▓ рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдП, рд╕рд░рд▓ рдХрджрдореЛрдВ рд╕реЗ рдпреБрдХреНрдд рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП рдФрд░ рдХрд▓ рдХреЗ рдЫрд╛рддреНрд░ рдХреЗ рд▓рд┐рдП рднреА рд╕рдордЭрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред 99% рдореЗрдВ рдХреЛрдИ рд╕рдорд╕реНрдпрд╛ рдирд╣реАрдВ рд╣реИ рдХрд┐ рдЕрдиреБрд░реЛрдз рдФрд░ рд░рд┐рд▓реАрдЬ рдкреВрд▓ рдХреА рд╡рд┐рдзрд╛рдирд╕рднрд╛ рдЕрдиреБрдХреНрд░рдорд┐рдХ рд╣реИ, рдФрд░ рд╕рдорд╛рдирд╛рдВрддрд░ рдирд╣реАрдВ рд╣реИред рдпрджрд┐ рд╕рдореАрдХреНрд╖рд╛ 1-2 рдкреАрдЖрд░ рд╕реЗ рдЕрдзрд┐рдХ рдЬрдорд╛ рдирд╣реАрдВ рд╣реЛрддреА рд╣реИ, рддреЛ рдПрдХ рд╕рд╛рде рд╡рд┐рдзрд╛рдирд╕рднрд╛рдУрдВ рдХрд╛ рдирд┐рд╖реЗрдз рдХрд╛рдлреА рдкрд░реНрдпрд╛рдкреНрдд рд╣реИред
рдФрд░ рд╕рдорд╛рдирд╛рдВрддрд░ рд▓реЙрдиреНрдЪ рдХреЗ рд▓рд┐рдП рд╣рдореЗрдВ рдЙрди рдард┐рдХрд╛рдиреЛрдВ рдпрд╛ рдпреЛрдЬрдирд╛рдУрдВ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ, рдЬрд┐рдирдореЗрдВ рдкреНрд░рддреНрдпреЗрдХ рд▓реЙрдиреНрдЪ рдХрд┐рдП рдЧрдП рдкрд░реАрдХреНрд╖рдг рдХреА рдЕрдирдиреНрдп рдкрд╣реБрдВрдЪ рд╣реЛрдЧреАред
рд╡рд┐рдХрд▓реНрдк рдПрдХ рдЧрддрд┐рд╢реАрд▓ рд░реВрдк рд╕реЗ рдЖрд╡рдВрдЯрд┐рдд рдХрд░рдирд╛ рд╣реИред рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рдПрдХ рд╕реНрдХреАрдорд╛ рдмрдирд╛рдирд╛ рддреЗрдЬ рд╣реИред API рдХреЗ рд╕рд╛рде рдХреНрд▓рд╛рдЙрдб рд╣реЛрдиреЗ рдкрд░, рдЖрдк рд╡рд╣рд╛рдВ рдПрдХ рдбреЗрдЯрд╛рдмреЗрд╕ рдЖрд╡рдВрдЯрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
рдпрджрд┐ рдЖрдк рдкреБрд░рд╛рдиреЗ рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЛ рд╣рдЯрд╛рдиреЗ рдХрд╛ рдХрд╛рдо рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдЖрдк рдбрд┐рд╕реНрдХ рд╕реНрдерд╛рди рдХреЛ рдЬрд▓реНрджреА рд╕реЗ рд╕рдорд╛рдкреНрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдЬрдм рдкрд░реАрдХреНрд╖рдг рдЧрд┐рд░ рдЬрд╛рддреЗ рд╣реИрдВ рдФрд░ рд╕рдВрд╕рд╛рдзрдиреЛрдВ рдХреЛ рдореБрдХреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП "рднреВрд▓ рдЬрд╛рддреЗ рд╣реИрдВ"ред рдпрд╣ "рдЬрдм," рдирд╣реАрдВ рддреЛ "рд╣реИред"
рд╡рд┐рдХрд▓реНрдк рджреЛ - рдПрдХ рдЕрд▓рдЧ рдкреНрд░рдмрдВрдзрди рд╕реЗрд╡рд╛ рдХреЗ рд╕рд╛рде рдПрдХ рдбреЗрдЯрд╛рдмреЗрд╕ / рд╕реНрдХреАрдорд╛ рдкреВрд▓ред рдмрд╛рд╣рд░ рдПрдкреАрдЖрдИ рдЪрд┐рдкрдХ рдЬрд╛рддреА рд╣реИ, рд╕рдордп рдХреЗ рд▓рд┐рдП рдЖрдзрд╛рд░ рджреЗрддреЗ рд╣реИрдВ, рдЕрд╡рдзрд┐ рд╕реЗ рдкрд╣рд▓реЗ рдирд┐: рд╢реБрд▓реНрдХ рдЖрдзрд╛рд░ рд╡рд╛рдкрд╕ рд▓реЗрддреЗ рд╣реИрдВред рдпрд╣ рдХреНрдпрд╛ рд▓реМрдЯреЗрдЧрд╛: рдПрдХ рдбреЗрдЯрд╛рдмреЗрд╕ рдпрд╛ рдмрд╕ рдереЛрдбрд╝рд╛ рд╕реНрдХреАрдорд╛ рдХреЗ рд╕рд╛рде рдПрдХ рд╕рдорд░реНрдкрд┐рдд рд╕рд░реНрд╡рд░, рдЗрд╕рд╕реЗ рдХреЛрдИ рдлрд░реНрдХ рдирд╣реАрдВ рдкрдбрд╝рддрд╛ред рдореБрдЦреНрдп рдмрд╛рдд рдпрд╣ рд╣реИ рдХрд┐ рд╕рдВрд╕рд╛рдзрди рд╣рдореЗрд╢рд╛ рдХреЗ рд▓рд┐рдП рдЦреЛ рдирд╣реАрдВ рдЬрд╛рдПрдЧрд╛ред
рд╡рд┐рдХрд▓реНрдк рддреАрди - рдЖрддреНрдо-рдирд┐рдпрдорди рдХреЗ рд▓рд┐рдП рдЖрдзрд╛рд░ / рдпреЛрдЬрдирд╛рдУрдВ рдХрд╛ рдПрдХ рдкреВрд▓ред рддрд╛рд▓реЗ рдФрд░ рдбреЗрдЯрд╛рдмреЗрд╕ рдкреВрд▓ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реА рдХреЗ рдЖрджрд╛рди-рдкреНрд░рджрд╛рди рдХреЗ рд▓рд┐рдП рдПрдХ рд╕рдВрд╕рд╛рдзрди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред
рдореИрдВ рдмрд╛рдж рдХреЗ рд╡рд┐рдХрд▓реНрдк рдкрд░ рдмрд╕ рдЧрдпрд╛, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдореЗрд░реЗ рд▓рд┐рдП рдЗрд╕реЗ рдЬрдХрдбрд╝рдирд╛ рдЖрд╕рд╛рди рд╣реИ рдФрд░ рдЗрд╕реЗ рд╕рдорд░реНрдерди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ рдЖрд╡рд╢реНрдпрдХ рдирд╣реАрдВ рд╣реИред рддрд░реНрдХ рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ рд╣реИ - рдХрдИ (рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП 10) рд╕рд░реНрдХрд┐рдЯ рдмрдирд╛рдП рдЬрд╛рддреЗ рд╣реИрдВ рдФрд░ рдЙрдиреНрд╣реЗрдВ рдЬреЛрдбрд╝рдиреЗ рдкрд░ рд╕рднреА рдЖрд╡рд╢реНрдпрдХ рдЬрд╛рдирдХрд╛рд░реА рд╕рд╛рдЭрд╛ рд╕рдВрд╕рд╛рдзрди рдореЗрдВ рдЬреЛрдбрд╝ рджреА рдЬрд╛рддреА рд╣реИ, рд╢реБрд░реВ рд╣реЛрдиреЗ рд╕реЗ рдкрд╣рд▓реЗ рдкреНрд░рддреНрдпреЗрдХ рдкрд░реАрдХреНрд╖рдг рдЙрджрд╛рд╣рд░рдг рдЕрдВрдд рдХреЗ рдмрд╛рдж рдПрдХ рдЪрд┐рд╣реНрди рдмрдирд╛рддрд╛ рд╣реИ - рдЗрд╕реЗ рд╣рдЯрд╛ рджреЗрддрд╛ рд╣реИред рдпрджрд┐ рдкрд░реАрдХреНрд╖рдг рдЕрдВрддрд┐рдо рд░реВрдк рджреЗрдиреЗ рд╕реЗ рдкрд╣рд▓реЗ рджреБрд░реНрдШрдЯрдирд╛рдЧреНрд░рд╕реНрдд рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ, рддреЛ рд╕рд░реНрдХрд┐рдЯ рдХреЛ рд╕рдорд╛рдкреНрддрд┐ рдХреЗ рд╕рдордп рдореБрдХреНрдд рдорд╛рдирд╛ рдЬрд╛рдПрдЧрд╛ред
рдкрдврд╝рдирд╛ рд╕реЗрдЯрд┐рдВрдЧреНрд╕:
project.ext.localConfig = new Properties() localConfig.load(file("${rootDir}/local.properties").newReader())
рдЧреНрд░реЗрдбрд┐рдВрдЧ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рд╕реЗ sql рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдбреНрд░рд╛рдЗрд╡рд░ рд▓реЛрдб рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ:
configurations { driver } dependencies { driver group: "oracle", name: "ojdbc6", version: "11.+" } task initDriver { doLast { ClassLoader loader = GroovyObject.class.classLoader configurations.driver.each { File file -> loader.addURL(file.toURL()) } } }
рдХрдиреЗрдХреНрд╢рди:
import groovy.sql.Sql project.ext.createSqlInstance = { return Sql.newInstance( url: localConfig["pool.db.url"], user: localConfig["pool.db.username"], password: localConfig["pool.db.password"], driver: localConfig["pool.db.driverClass"]) }
рдбреЗрдЯрд╛рдмреЗрд╕ рддрд╛рд▓рд┐рдХрд╛ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╕реВрдЪрдирд╛ рдХрд╛ рдЖрджрд╛рди-рдкреНрд░рджрд╛рди рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рд╕рдВрджрд░реНрдн рддрд╛рд▓рд┐рдХрд╛ рдХрд╛ рдкреНрд░рд╛рд░рдВрдн (рдпрд╣ рдПрдХ рдмрд╛рд░ рдХрд╛рдо рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП, рдлрд┐рд░ рддрд╛рд▓рд┐рдХрд╛ рд╕рдордп рдХреЗ рдЕрдВрдд рддрдХ рд░рд╣рддреА рд╣реИ):
task initDbPool { dependsOn initDriver doLast { Integer poolSize = 10 Sql sql = createSqlInstance() as Sql String tableName = localConfig["pool.db.referenceTable"] String baseName = localConfig["pool.db.baseName"] String basePass = localConfig["pool.db.basePass"] String token = "{id}" List tableExists = sql.rows("select table_name from all_tables where table_name=?", [tableName]) assert tableExists.isEmpty() sql.execute(""" CREATE TABLE ${tableName} ( ID NUMBER(2) NOT NULL PRIMARY KEY, METADATA VARCHAR2(200) NOT NULL, PROCESSED TIMESTAMP NULL, GUID VARCHAR2(36) NULL) """, []) for (Integer i = 0 ; i < poolSize ; i++) { String username = baseName.replace(token, i.toString()) String password = basePass.replace(token, i.toString()) sql.execute(""" CREATE USER ${username} IDENTIFIED BY "${password}" DEFAULT TABLESPACE USERS TEMPORARY TABLESPACE TEMP PROFILE DEFAULT QUOTA UNLIMITED ON USERS """, []) sql.execute("grant connect to ${username}", []) sql.execute("grant create sequence to ${username}", []) sql.execute("grant create session to ${username}", []) sql.execute("grant create table to ${username}", []) String metadata = JsonOutput.toJson([ "app.db.driverClass": localConfig["pool.db.driverClass"], "app.db.url": localConfig["pool.db.url"], "app.db.username": username, "app.db.password": password ]) sql.execute(""" INSERT INTO ${tableName} (id, metadata) values (?, ?) """, [i, metadata]) } } }
рдбрд┐рдмрдЧрд┐рдВрдЧ рдФрд░ рдЕрд╕реЗрдВрдмрд▓реА рдХреЗ рд▓рд┐рдП рдбреЗрд╡рд▓рдкрд░реНрд╕ рдХреА рдЕрдкрдиреА рдпреЛрдЬрдирд╛рдПрдВ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рдкреВрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдмрдВрдж рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП:
project.ext.isCiBuild = { Boolean.parseBoolean(localConfig['pool.db.enabled'] as String) }
рдЖрдзрд╛рд░ рд▓реЗрдВ рдФрд░ рдореБрдХреНрдд рдХрд░реЗрдВ:
task lockDb { dependsOn initDriver onlyIf isCiBuild doLast { project.ext.lockUid = UUID.randomUUID().toString() String tableName = localConfig["pool.db.referenceTable"] Sql sql = createSqlInstance() as Sql sql.executeUpdate("""UPDATE ${tableName} SET GUID = ?, PROCESSED = SYSDATE WHERE ID IN ( SELECT ID FROM ( SELECT ID, ROW_NUMBER() OVER (ORDER BY PROCESSED) AS RN FROM ${tableName} WHERE GUID IS NULL OR PROCESSED < (SYSDATE - NUMTODSINTERVAL(?, 'MINUTE')) ) WHERE RN = 1 ) """, [lockUid, 15]) def meta = sql.firstRow("SELECT METADATA FROM ${tableName} WHERE GUID = ?", [lockUid]) assert meta != null, "No free databases in pool" def slurper = new JsonSlurper() Map metadata = slurper.parseText(meta["METADATA"] as String) as Map localConfig.putAll(metadata) logger.info("Database locked, {}", metadata) } } task unlockDb { dependsOn lockDb
рдпрджрд┐ рдЖрдк рдПрдХ рдкрдВрдХреНрддрд┐ рдореЗрдВ 2 рдмрд╛рд░ рд╡рд┐рдзрд╛рдирд╕рднрд╛ рдХреЛ рдкреВрд░рд╛ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рд╡рд┐рднрд┐рдиреНрди рдпреЛрдЬрдирд╛рдУрдВ рдХрд╛ рдЪрдпрди рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдФрд░ рд╕рдВрдкрддреНрддрд┐ рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреЛ рдЗрдХрдЯреНрдард╛ рдХрд░рддреЗ рд╕рдордп рд╡рд┐рднрд┐рдиреНрди рдорд╛рди рдмрдиреЗ рд░рд╣реЗрдВрдЧреЗред рд╕реНрдерд╛рдиреАрдп рд▓реЙрдиреНрдЪ рдХреЗ рд▓рд┐рдП, рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рд╕реНрдерд┐рд░ рд╣реИрдВред
configure([processResources, processTestResources]) { Task t -> if (project.ext.isCiBuild()) { t.outputs.upToDateWhen { false } } t.filesMatching('**/*.properties') { filter(ReplaceTokens, tokens: localConfig, beginToken: '${', endToken: '}') } }
рд░реЛрд▓рдмреИрдХ рдХреЗ рдкрд░реАрдХреНрд╖рдг рдХреЗ рд▓рд┐рдП рдХрд╛рд░реНрдп:
task restoreAfterRollbackTest(type: LiquibaseTask) { command = 'update' } task rollbackTest(type: LiquibaseTask) { dependsOn lockDb command = 'rollback' requiresValue = true doFirst { project.ext.liquibaseCommandValue = localConfig['test.rollback.tag'] } doLast { project.ext.liquibaseCommandValue = null } }
рдФрд░ рдирд┐рд╖реНрдкрд╛рджрди рдЖрджреЗрд╢ рд╕реЗрдЯ рдХрд░реЗрдВ:
configure([project]) { tasks.withType(LiquibaseTask.class) { LiquibaseTask t -> logger.info("Liquibase task {} must run after {}", t.getName(), configLiquibase.getName()) (t as Task).dependsOn configLiquibase if (isCiBuild()) { logger.info("Liquibase task {} must run after {}", t.getName(), lockDb.getName()) (t as Task).dependsOn lockDb (t as Task).finalizedBy unlockDb } }
рдбреЗрдЯрд╛рдмреЗрд╕ рдЖрд╡рдВрдЯрди рдФрд░ рд░реЛрд▓рдмреИрдХ рдкрд░реАрдХреНрд╖рдг рдХреЛ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЗ рдЕрдВрджрд░ рд░рдЦрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рд╕рднреА рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЗ рдкрд╣рд▓реЗ рдФрд░ рдмрд╛рдж рдореЗрдВ рдХреЛрдб рдЪрд▓рд╛рдиреЗ рдХреЗ рддрд░реАрдХреЗ: Junit5 рдореЗрдВ, рдпрд╣ TestAG рдореЗрдВ, рдЗрд╕рд╕реЗ рдкрд╣рд▓реЗ рд╣реИред
рдкреНрд░рд╢реНрди "рдЬрд╛рд╡рд╛ рдкреНрд░реЛрдЧреНрд░рд╛рдорд░ рджреНрд╡рд╛рд░рд╛ рдПрд╕рдХреНрдпреВрдПрд▓ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХреНрдпреЛрдВ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП" рдЗрд╕рдХрд╛ рдЙрддреНрддрд░ рд╣реИ рдХрд┐ рдХрд┐рд╕реА рднреА рдХреЛрдб рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред рдХреБрдЫ рдЕрдкрд╡рд╛рдж рд╣реИрдВ рдФрд░ рдХреБрдЫ рдХреЛрдб рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рд╕рдордп рдкрд░ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рддрд░реНрдХрд╣реАрди рд╣реИрдВред
рдореИрдВ рдЬрд╛рдирдирд╛ рдЪрд╛рд╣реВрдВрдЧрд╛ рдХрд┐ рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЗ рд╕рд╛рде рдЗрдВрдЯрд░реИрдХреНрд╢рди рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдХреА рд╕рдорд╕реНрдпрд╛ рдЕрдиреНрдп рдкреНрд░реЛрдЧреНрд░рд╛рдорд░ рджреНрд╡рд╛рд░рд╛ рдХреИрд╕реЗ рд╣рд▓ рдХреА рдЬрд╛рддреА рд╣реИ? рдХреНрдпрд╛ рдХрдВрдЯреЗрдирд░ рд╣рд░ рдШрд░ рдореЗрдВ рдЖ рдЧрдпрд╛ рд╣реИ, рдпрд╛ рдПрдХреАрдХрд░рдг рдкрд░реАрдХреНрд╖рдг рдкрд░реАрдХреНрд╖рдХреЛрдВ рдХреЗ рдХрдВрдзреЛрдВ рдкрд░ рд╣реИ?
рдкреВрд░реА рд▓рд┐рд╕реНрдЯрд┐рдВрдЧ import groovy.json.JsonOutput import groovy.json.JsonSlurper import groovy.sql.Sql import org.apache.tools.ant.filters.ReplaceTokens import org.liquibase.gradle.LiquibaseTask plugins { id 'java' id 'org.liquibase.gradle' version '2.0.1' } configurations { driver } repositories { jcenter() mavenCentral() maven { url = "http://www.datanucleus.org/downloads/maven2/" } } dependencies { implementation 'com.google.guava:guava:27.0.1-jre' implementation 'org.springframework:spring-core:5.1.7.RELEASE' implementation 'org.springframework:spring-context:5.1.7.RELEASE' implementation 'org.springframework:spring-jdbc:5.1.7.RELEASE' testImplementation 'junit:junit:4.12' testImplementation 'org.springframework:spring-test:5.1.7.RELEASE' testRuntime 'oracle:ojdbc6:11.+' liquibaseRuntime 'org.liquibase:liquibase-core:3.6.1' liquibaseRuntime 'oracle:ojdbc6:11.+' liquibaseRuntime 'org.yaml:snakeyaml:1.24' driver group: "oracle", name: "ojdbc6", version: "11.+" } project.ext.localConfig = new Properties() localConfig.load(file("${rootDir}/local.properties").newReader()) project.ext.isCiBuild = { Boolean.parseBoolean(localConfig['pool.db.enabled'] as String) } project.ext.doRollbackTest = { Boolean.parseBoolean(localConfig['test.rollback.enabled'] as String) } task configLiquibase { doLast { liquibase { activities { testdb { changeLogFile 'changelog.yaml' url localConfig['app.db.url'] driver localConfig['app.db.driverClass'] username localConfig['app.db.username'] password localConfig['app.db.password'] logLevel 'debug' classpath "${project.projectDir}/db" contexts 'main' } runList = 'testdb' } } } } task initDriver { doLast { ClassLoader loader = GroovyObject.class.classLoader configurations.driver.each { File file -> loader.addURL(file.toURL()) } } } project.ext.createSqlInstance = { return Sql.newInstance( url: localConfig["pool.db.url"], user: localConfig["pool.db.username"], password: localConfig["pool.db.password"], driver: localConfig["pool.db.driverClass"]) } task initDbPool { dependsOn initDriver doLast { Integer poolSize = 10 Sql sql = createSqlInstance() as Sql String tableName = localConfig["pool.db.referenceTable"] String baseName = localConfig["pool.db.baseName"] String basePass = localConfig["pool.db.basePass"] String token = "{id}" List tableExists = sql.rows("select table_name from all_tables where table_name=?", [tableName]) assert tableExists.isEmpty() sql.execute(""" CREATE TABLE ${tableName} ( ID NUMBER(2) NOT NULL PRIMARY KEY, METADATA VARCHAR2(200) NOT NULL, PROCESSED TIMESTAMP NULL, GUID VARCHAR2(36) NULL) """, []) for (Integer i = 0 ; i < poolSize ; i++) { String username = baseName.replace(token, i.toString()) String password = basePass.replace(token, i.toString()) sql.execute(""" CREATE USER ${username} IDENTIFIED BY "${password}" DEFAULT TABLESPACE USERS TEMPORARY TABLESPACE TEMP PROFILE DEFAULT QUOTA UNLIMITED ON USERS """, []) sql.execute("grant connect to ${username}", []) sql.execute("grant create sequence to ${username}", []) sql.execute("grant create session to ${username}", []) sql.execute("grant create table to ${username}", []) String metadata = JsonOutput.toJson([ "app.db.driverClass": localConfig["pool.db.driverClass"], "app.db.url": localConfig["pool.db.url"], "app.db.username": username, "app.db.password": password ]) sql.execute(""" INSERT INTO ${tableName} (id, metadata) values (?, ?) """, [i, metadata]) } } } task lockDb { dependsOn initDriver onlyIf isCiBuild doLast { project.ext.lockUid = UUID.randomUUID().toString() String tableName = localConfig["pool.db.referenceTable"] Sql sql = createSqlInstance() as Sql sql.executeUpdate("""UPDATE ${tableName} SET GUID = ?, PROCESSED = SYSDATE WHERE ID IN ( SELECT ID FROM ( SELECT ID, ROW_NUMBER() OVER (ORDER BY PROCESSED) AS RN FROM ${tableName} WHERE GUID IS NULL OR PROCESSED < (SYSDATE - NUMTODSINTERVAL(?, 'MINUTE')) ) WHERE RN = 1 ) """, [lockUid, 15]) def meta = sql.firstRow("SELECT METADATA FROM ${tableName} WHERE GUID = ?", [lockUid]) assert meta != null, "No free databases in pool" def slurper = new JsonSlurper() Map metadata = slurper.parseText(meta["METADATA"] as String) as Map localConfig.putAll(metadata) logger.info("Database locked, {}", metadata) } } task unlockDb { dependsOn lockDb
pool.db.enabled=false test.rollback.enabled=true pool.db.driverClass=oracle.jdbc.driver.OracleDriver pool.db.url=jdbc:oracle:thin:@localhost:1527:ORCLCDB pool.db.username=SYSTEM pool.db.password=Oradoc_db1 pool.db.referenceTable=c