ਮਲਟੀ-ਸਟੈਪ ਵਰਕਫਲੋ ਲਈ Postgres ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਸਿੱਖੋ: ਅਪਡੇਟਾਂ ਨੂੰ ਸੁਰੱਖਿਅਤ ਤਰੀਕੇ ਨਾਲ ਗਰੁੱਪ ਕਰਨ, ਅਧੂਰੇ ਲਿਖਤ ਤੋਂ ਬਚਣ, ਰੀਟ੍ਰਾਈਜ਼ ਸੰਭਾਲਣ ਅਤੇ ਡੇਟਾ ਇੱਕਸਾਰ ਰੱਖਣ ਦਾ ਤਰੀਕਾ।

ਅਧਿਕਤਰ ਅਸਲ ਫੀਚਰ ਇੱਕ ਹੀ ਡੇਟਾਬੇਸ ਅਪਡੇਟ ਨਹੀਂ ਹੁੰਦੇ। ਇਹ ਛੋਟੀ ਲੜੀ ਹੁੰਦੀ ਹੈ: ਇੱਕ ਰੋ ਨਹੀਂ, ਬੈਲੈਂਸ ਅਪਡੇਟ, ਸਟੇਟਸ ਨਿਸ਼ਾਨ, ਆਡਿਟ ਰਿਕਾਰਡ, ਸ਼ਾਇਦ ਕੋਈ ਜੌਬ ਕਤਾਰ ਵਿੱਚ ਰੱਖਣਾ। ਅਧੂਰਾ ਲਿਖਤ ਉਸ ਵੇਲੇ ਹੁੰਦਾ ਹੈ ਜਦੋਂ ਇਹਨਾਂ ਵਿੱਚੋਂ ਸਿਰਫ਼ ਕੁਝ స్టੈਪ ਡੇਟਾਬੇਸ ਤੱਕ ਪਹੁੰਚਦੇ ਹਨ।
ਇਹ ਉਸ ਵੇਲੇ ਸਾਹਮਣੇ ਆਉਂਦਾ ਹੈ ਜਦੋਂ ਕਿਸੇ ਚੀਜ਼ ਨੇ ਚੇਨ ਨੂੰ ਰੋਕ ਦਿੱਤਾ: ਸਰਵਰ ਐਰਰ, ਤੁਹਾਡੇ ਐਪ ਅਤੇ Postgres ਦਰਮਿਆਨ timeout, ਸਟੈਪ 2 ਤੋਂ ਬਾਅਦ crash, ਜਾਂ retry ਜੋ ਸਟੈਪ 1 ਨੂੰ ਮੁੜ ਚਲਾਉਂਦਾ ਹੈ। ਹਰ ਬਿਆਨ ਆਪਣੇ ਆਪ ਠੀਕ ਹੋ ਸਕਦਾ ਹੈ। ਪਰ ਜਦੋਂ ਵਰਕਫਲੋ ਅਰਧ-ਰੂਪ ਵਿੱਚ ਰੁਕ ਜਾਂਦੀ ਹੈ, ਤਦ ਸਮੱਸਿਆ ਬਣਦੀ ਹੈ।
ਆਮ ਤੌਰ 'ਤੇ ਤੁਸੀਂ ਇਸਨੂੰ ਤੁਰੰਤ ਵੇਖ ਸਕਦੇ ਹੋ:
ਇਕ ਉਦਾਹਰਣ: plan upgrade ਇੱਕ ਗਾਹਕ ਦੀ ਯੋਜਨਾ ਅਪਡੇਟ ਕਰਦਾ ਹੈ, payment ਰਿਕਾਰਡ ਜੋੜਦਾ ਹੈ, ਅਤੇ ਲਭਣ ਵਾਲੇ credits ਵਧਾਉਂਦਾ ਹੈ। ਜੇ ਐਪ payment ਸੇਵ ਕਰਨ ਤੋਂ ਬਾਅਦ crash ਹੋ ਜਾਵੇ ਪਰ credits ਜੋੜਨ ਤੋਂ ਪਹਿਲਾਂ, support ਇੱਕ ਟੇਬਲ ਵਿੱਚ 'paid' ਦੇਖੇਗਾ ਤੇ ਦੂਜੇ ਵਿੱਚ 'no credits'। ਜੇ client retry ਕਰੇ, ਤਾਂ ਤੁਸੀਂ payment ਦੋ ਵਾਰੀ ਵੀ ਰਿਕਾਰਡ ਕਰ ਸਕਦੇ ਹੋ।
ਮਕਸਦ ਸਧਾਰਨ ਹੈ: ਵਰਕਫਲੋ ਨੂੰ ਇੱਕ ਸਿੰਗਲ ਸਵਿੱਚ ਵਾਂਗ ਸੌਂਭੋ। ਜਾਂ ਤਾਂ ਹਰ ਸਟੈਪ ਸਫਲ ਹੋਵੇ, ਜਾਂ ਕੋਈ ਵੀ ਨਾ ਹੋਵੇ — ਤਾਂ ਤੁਸੀਂ ਅਰਧ-ਕੰਮ ਨੂੰ ਸਟੋਰ ਨਹੀਂ ਕਰੋਗੇ।
ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਡੇਟਾਬੇਸ ਦਾ ਤਰੀਕਾ ਹੈ ਇਹ ਕਹਿਣ ਦਾ: ਇਨ੍ਹਾਂ ਕਦਮਾਂ ਨੂੰ ਇੱਕ ਯੂਨਿਟ ਵਜੋਂ ਲਓ। ਜਾਂ ਤਾਂ ਸਾਰੇ ਬਦਲਾਅ ਹੋਣ, ਜਾਂ ਕੋਈ ਵੀ ਨਹੀਂ। ਜਦੋਂ ਤੁਹਾਡੇ ਵਰਕਫਲੋ ਨੂੰ ਇੱਕ ਤੋਂ ਵੱਧ ਅਪਡੇਟ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ, ਇਹ ਮਹੱਤਵਪੂਰਨ ਹੁੰਦਾ ਹੈ — ਜਿਵੇਂ ਕਿ ਇੱਕ ਰੋ ਬਣਾਉਣਾ, ਬੈਲੈਂਸ ਅਪਡੇਟ ਕਰਨਾ, ਅਤੇ ਆਡਿਟ ਰਿਕਾਰਡ ਲਿਖਣਾ।
ਪੈਸਾ ਇੱਕ ਖਾਤੇ ਤੋਂ ਦੂਜੇ ਵਿੱਚ ਭੇਜਣ ਦੀ ਸੋਚੋ। ਤੁਹਾਨੂੰ Account A ਤੋਂ ਘਟਾਉਣਾ ਅਤੇ Account B ਵਿੱਚ ਜੋੜਨਾ ਪਏਗਾ। ਜੇ ਐਪ ਪਹਿਲੇ ਕਦਮ ਤੋਂ ਬਾਅਦ crash ਹੋ ਜਾਵੇ, ਤਾਂ ਤੁਸੀਂ ਸਿਸਟਮ ਨੂੰ ਸਿਰਫ਼ ਘਟਾਉਣਾ ਯਾਦ ਰੱਖਣ ਨਹੀਂ ਦੇਣਾ ਚਾਹੁੰਦੇ।
ਜਦ ਤੂੰ commit ਕਰਦਾ/ਕਰਦੀ ਹੈ, ਤੂੰ Postgres ਨੂੰ ਕਹਿੰਦਾ/ਕਹਿੰਦੀ ਹੈ: ਇਸ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਵਿੱਚ ਮੈਂ ਜੋ ਕੀਤਾ ਉਹ ਰੱਖੋ। ਸਾਰੇ ਬਦਲਾਅ ਪੱਕੇ ਅਤੇ ਹੋਰ ਸੈਸ਼ਨਾਂ ਲਈ ਦਿੱਸਣਯੋਗ ਹੋ ਜਾਂਦੇ ਹਨ।
ਜਦ ਤੂੰ rollback ਕਰਦਾ/ਕਰਦੀ ਹੈ, ਤੂੰ Postgres ਨੂੰ ਕਹਿੰਦਾ/ਕਹਿੰਦੀ ਹੈ: ਇਸ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਵਿੱਚ ਮੈਂ ਜੋ ਕੀਤਾ ਉਹ ਭੁੱਲ ਜਾਓ। Postgres ਉਹ ਲਿਖਤ ਵਾਪਸ ਕਰ ਦੇਂਦਾ ਹੈ ਜਿਵੇਂ ਕਿ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਕਦੇ ਨਹੀਂ ਹੋਇਆ।
ਇੱਕ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਦੇ ਅੰਦਰ, Postgres ਗਰੰਟੀ ਦਿੰਦਾ ਹੈ ਕਿ ਤੁਸੀਂ commit ਤੱਕ ਹੋਰ ਸੈਸ਼ਨਾਂ ਨੂੰ ਅਧੂਰੇ ਨਤੀਜੇ ਨਹੀਂ ਦਿਖਾਓਗੇ। ਜੇ ਕੁਝ ਫੇਲ ਹੋ ਜਾਂਦਾ ਅਤੇ ਤੁਸੀਂ rollback ਕਰਦੇ ਹੋ, ਡੇਟਾਬੇਸ ਉਸ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਦੀਆਂ ਲਿਖਤਾਂ ਸਾਫ਼ ਕਰ ਦੇਂਦਾ ਹੈ।
ਇੱਕ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਮਾੜੀ ਵਰਕਫਲੋ ਡਿਜ਼ਾਇਨ ਨੂੰ ਠੀਕ ਨਹੀਂ ਕਰਦਾ। ਜੇ ਤੁਸੀਂ ਗਲਤ ਰਕਮ ਘੱਟ ਕਰਦੇ ਹੋ, ਗਲਤ user ID ਵਰਤਦੇ ਹੋ, ਜਾਂ ਕੋਈ ਜ਼ਰੂਰੀ ਚੈਕ ਛੱਡ ਦਿੰਦੇ ਹੋ, Postgres ਗਲਤ ਨਤੀਜਾ ਫੈਥਫੁੱਲੀ ਤਰੀਕੇ ਨਾਲ commit ਕਰ ਦੇਵੇਗਾ। ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਹਰ ਬਿਜ਼ਨਸ-ਲੇਵਲ ਟੱਕਰ (ਜਿਵੇਂ overselling inventory) ਨੂੰ ਅਕਸਰ ਆਪਣੇ ਆਪ ਰੋਕਦੇ ਨਹੀਂ ਜਦ ਤੱਕ ਤੁਹਾਡੇ ਕੋਲ ਸਹੀ constraints, locks, ਜਾਂ isolation level ਨਹੀਂ ਹੁੰਦੇ।
ਜਦੋਂ ਤੁਸੀਂ ਇੱਕ ਹੀ ਅਸਲ-ਦਿਨ ਦੇ ਕੰਮ ਲਈ ਇੱਕ ਤੋਂ ਵੱਧ ਟੇਬਲ (ਜਾਂ ਇੱਕ ਤੋਂ ਵੱਧ ਰੋ) ਅਪਡੇਟ ਕਰਦੇ ਹੋ, ਤਾਂ ਇਹ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਲਈ ਉਮੀਦਵਾਰ ਹੁੰਦਾ ਹੈ। ਮਕਸਦ ਇੱਕੋ ਰਿਹਾ: ਜਾਂ ਤਾਂ ਸਭ ਕੁਝ ਹੋਵੇ, ਜਾਂ ਕੁਛ ਵੀ ਨਾ ਹੋਵੇ।
ਆਰਡਰ ਫਲੋ ਇਸ ਦਾ ਕਲਾਸਿਕ ਮਿਸਾਲ ਹੈ। ਤੁਸੀਂ ਇੱਕ orders ਰੋ ਬਣਾਉ ਸਕਦੇ ਹੋ, inventory ਰਿਜ਼ਰਵ ਕਰ ਸਕਦੇ ਹੋ, payment ਲੈ ਸਕਦੇ ਹੋ, ਫਿਰ order ਨੂੰ paid ਦਿੱਸਾ ਦੇ ਸਕਦੇ ਹੋ। ਜੇ payment ਸਫਲ ਹੋ ਜਾਂਦਾ ਪਰ status update fail ਹੋ ਜਾਵੇ, ਤੁਹਾਡੇ ਕੋਲ ਪੈਸਾ ਕੈਪਚਰ ਹੋਇਆ ਹੋ ਸਕਦਾ ਹੈ ਪਰ order ਅਜੇ ਵੀ unpaid ਦਿਖਾਈ ਦੇ ਸਕਦਾ ਹੈ। ਜੇ order ਰੋ ਬਣੀ ਪਰ stock ਰਿਜ਼ਰਵ ਨਹੀਂ ਹੋਇਆ, ਤਾਂ ਤੁਸੀਂ ਉਹ ਆਈਟਮ ਵੇਚ ਸਕਦੇ ਹੋ ਜੋ ਤੁਹਾਡੇ ਕੋਲ ਨਹੀਂ।
ਯੂਜ਼ਰ onboarding ਵੀ ਅਜਿਹੇ ਹੀ ਤਰੀਕੇ ਨਾਲ ਟੁੱਟ ਸਕਦੀ ਹੈ। ਯੂਜ਼ਰ ਬਣਾਉਣਾ, profile ਰਿਕਾਰਡ ਇਨਸਰਟ ਕਰਨਾ, roles ਨਿਰਧਾਰਤ ਕਰਨਾ, ਅਤੇ welcome email ਭੇਜਣ ਲਈ enqueue ਕਰਨਾ — ਇਹ ਸਭ ਇਕ ਤਰਕਿਕੀ ਇਕਾਈ ਹੈ। ਗਰੁੱਪਿੰਗ ਨਾ ਕਰਨ 'ਤੇ ਤੁਹਾਡੇ ਕੋਲ ਇੱਕ ਐਸਾ ਯੂਜ਼ਰ ਹੋ ਸਕਦਾ ਹੈ ਜੋ ਲੌਗਇਨ ਕਰ ਸਕਦਾ ਪਰ ਕੋਇ permissions ਨਹੀਂ, ਜਾਂ ਇੱਕ ਪ੍ਰੋਫਾਇਲ ਹੈ ਪਰ ਕੋਈ ਯੂਜ਼ਰ ਨਹੀਂ।
ਬੈਕ-ਆਫਿਸ ਕਾਰਵਾਈਆਂ ਵੱਖ-ਵੱਖ ਹਾਲਤਾਂ ਵਿੱਚ 'paper trail + state change' ਦੀ ਸਖਤ ਲੋੜ ਰੱਖਦੀਆਂ ਹਨ। ਇੱਕ ਬੇਨਤੀ ਮਨਜ਼ੂਰ ਕਰਨਾ, ਆਡਿਟ ਐਂਟਰੀ ਲਿਖਣਾ, ਅਤੇ ਬੈਲੈਂਸ ਅਪਡੇਟ ਕਰਨਾ ਇਕੱਠੇ ਸਫਲ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ। ਜੇ ਬੈਲੈਂਸ ਬਦਲਿਆ ਪਰ ਆਡਿਟ ਲਾਗ ਗਾਇਬ ਹੈ, ਤਾਂ ਤੁਹਾਡੇ ਕੋਲ ਇਹ ਨਹੀਂ ਰਹਿੰਦਾ ਕਿ ਕਿਸਨੇ ਕੀ ਅਤੇ ਕਿਉਂ ਕੀਤਾ।
ਬੈਕਗਰਾਊਂਡ ਜੌਬ ਵੀ ਲਾਭਦੇਹ ਹਨ, ਖਾਸ ਕਰਕੇ ਜਦੋਂ ਤੁਸੀਂ ਇੱਕ ਵਰਕ ਆਈਟਮ ਨਾਲ ਕਈ ਕਦਮ ਨਿਪਟਾਉਂਦੇ ਹੋ: ਆਈਟਮ ਕਲੇਮ ਕਰੋ ਤਾਂ ਕਿ ਦੋ ਵਰਕਰ ਇੱਕੋ ਕੰਮ ਨਾ ਕਰਨ, ਬਿਜ਼ਨਸ ਅਪਡੇਟ ਲਗੂ ਕਰੋ, ਫਿਰ ਰਿਪੋਰਟਿੰਗ ਅਤੇ ਰੀਟ੍ਰਾਈ ਲਈ ਨਤੀਜਾ ਰਿਕਾਰਡ ਕਰੋ, ਅਤੇ ਆਖਿਰ 'ਆਈਟਮ ਮੁਕੰਮਲ' (ਜਾਂ ਫੇਲ ਹੋਣ ਤੇ ਕਾਰਨ) ਮਾਰਕ ਕਰੋ। ਜੇ ਇਹ ਕਦਮ ਇਕ ਦੂਜੇ ਤੋਂ ਹੋਟ-ਰੇ, ਤਾਂ retries ਅਤੇ concurrency ਗੜਬੜ ਪੈਦਾ ਕਰਦੇ ਹਨ।
ਬਹੁ-ਕਦਮੀ ਫੀਚਰ ਤਦ ਤੋਟਦੇ ਹਨ ਜਦੋਂ ਤੁਸੀਂ ਉਹਨਾਂ ਨੂੰ ਇੱਕ ਢੇਰ ਅਜ਼ਾਦ ਅਪਡੇਟਾਂ ਵਾਂਗ ਸਮਝਦੇ ਹੋ। ਜਦੋਂ ਤੁਸੀਂ ਡੇਟਾਬੇਸ ਕਲਾਇੰਟ ਖੋਲ੍ਹਦੇ ਹੋ, ਪਹਿਲਾਂ ਵਰਕਫਲੋ ਨੂੰ ਇੱਕ ਛੋਟੀ ਕਹਾਣੀ ਵਾਂਗ ਲਿਖੋ ਜਿਸਦਾ ਇੱਕ ਸਪਸ਼ਟ ਫਿਨਿਸ਼ ਲਾਈਨ ਹੋਵੇ: ਉਪਭੋਗਤਾ ਲਈ 'done' ਦਾ ਕੀ ਮਤਲਬ ਹੈ?
ਪਹਿਲਾਂ ਕਦਮ ਸਧਾਰਨ ਭਾਸ਼ਾ ਵਿੱਚ ਲਿਖੋ, ਫਿਰ ਇੱਕ ਸਪਸ਼ਟ success condition ਨਿਰਧਾਰਤ ਕਰੋ। ਉਦਾਹਰਨ: 'Order ਬਣਿਆ ਹੈ, inventory ਰਿਜ਼ਰਵ ਹੋਇਆ ਹੈ, ਅਤੇ ਯੂਜ਼ਰ ਨੂੰ order confirmation number ਦਿੱਤਾ ਗਿਆ ਹੈ।' ਇਸ ਤੋਂ ਘੱਟ ਕੁਝ ਵੀ success ਨਹੀਂ।
ਅਗਲਾ ਕਦਮ: ਡੇਟਾਬੇਸ ਕੰਮ ਅਤੇ ਬਾਹਰੀ ਕੰਮ ਵਿਚਕਾਰ ਕਠੋਰ ਲਕੀਰ ਖਿੱਚੋ। ਡੇਟਾਬੇਸ ਕਦਮ ਉਹ ਹਨ ਜੋ ਤੁਸੀਂ transactions ਨਾਲ ਬਚਾ ਸਕਦੇ ਹੋ। ਬਾਹਰੀ ਕਾਲਾਂ ਜਿਵੇਂ card payments, emails ਭੇਜਣਾ, ਜਾਂ ਤੀਜੇ ਪਾਸੇ APIs ਦੇ ਕਾਲ ਆਮ ਤੌਰ 'ਤੇ ਸਲੋ ਅਤੇ ਅਣਪੇਸ਼ੀਦ ਹੁੰਦੀਆਂ ਹਨ, ਅਤੇ ਤੁਸੀਂ ਆਮ ਤੌਰ 'ਤੇ ਉਨਾਂ ਨੂੰ ਰੋਲਬੈਕ ਨਹੀਂ ਕਰ ਸਕਦੇ।
ਇੱਕ ਸਧਾਰਨ ਯੋਜਨਾ: ਕਦਮਾਂ ਨੂੰ ਦੋ ਹਿੱਸਿਆਂ ਵਿੱਚ ਵੰਡੋ: (1) ਜਿਸਨੂੰ all-or-nothing ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ, (2) ਜੋ commit ਦੇ ਬਾਅਦ ਹੋ ਸਕਦੀ ਹੈ।
ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਵਿੱਚ ਸਿਰਫ ਉਹੀ ਕਦਮ ਰੱਖੋ ਜੋ ਇਕੱਠੇ consistent ਰਹਿਣੇ ਲਾਜ਼ਮੀ ਹਨ:
ਸਾਈਡ-ਇਫੈਕਟਸ ਨੂੰ ਬਾਹਰ ਲਿਜਾਓ। ਉਦਾਹਰਨ ਲਈ, order commit ਕਰਨ ਤੋਂ ਬਾਅਦ confirmation email ਭੇਜੋ, ਅਤੇ ਇਸ ਲਈ ਇੱਕ outbox ਰਿਕਾਰਡ ਲਿਖੋ ਜੋ commit ਵਿੱਚ ਹੀ ਹੋਵੇ।
ਹਰ ਕਦਮ ਲਈ ਇਹ ਲਿਖੋ ਕਿ ਅਗਲੇ ਕਦਮ ਦੇ fail ਹੋਣ 'ਤੇ ਕੀ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ। 'Rollback' ਦਾ ਮਤਲਬ ਡੇਟਾਬੇਸ rollback ਵੀ ਹੋ ਸਕਦਾ ਹੈ, ਜਾਂ compensating action ਵੀ ਹੋ ਸਕਦੀ ਹੈ।
ਉਦਾਹਰਨ: ਜੇ payment ਸਫਲ ਹੋ ਗਿਆ ਪਰ inventory reservation fail ਹੋ ਜਾਂਦੀ, ਤਾਂ ਪਹਿਲਾਂ ਤੈਅ ਕਰੋ ਕਿ ਤੁਸੀਂ ਤੁਰੰਤ ਰਿਫੰਡ ਕਰੋਗੇ, ਜਾਂ order ਨੂੰ 'payment captured, awaiting stock' ਮਾਰਕ ਕਰਕੇ asynchronous ਤੌਰ 'ਤੇ ਹੱਲ ਕਰੋਗੇ।
ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ Postgres ਨੂੰ ਕਹਿੰਦਾ: ਇਨ੍ਹਾਂ ਕਦਮਾਂ ਨੂੰ ਇੱਕ ਯੂਨਿਟ ਵਜੋਂ ਲਵੋ। ਜਾਂ ਤਾਂ ਸਾਰੇ ਹੋ ਜਾਣ, ਜਾਂ ਕੋਈ ਵੀ ਨਾ ਹੋਣ — ਇਹ ਸਧਾਰਨ ਤਰੀਕਾ ਹੈ ਅਧੂਰੇ ਲਿਖਤ ਤੋਂ ਬਚਣ ਦਾ।
شروع تو آخر تک ایک ہی ڈیٹابیس کنکشن (ایاک سیشن) کا استعمال کریں۔ اگر آپ مختلف کنیکشنز میں قدم پھیلائیں گے تو Postgres تمام یا کچھ کے نتیجے کی ضمانت نہیں دے سکتا۔
ترتیب سیدھی ہے: begin، ضروری reads اور writes چلائیں، اگر سب کامیاب ہوں تو commit، ورنہ rollback کر کے واضح error واپس کریں۔
ਇੱਥੇ SQL ਵਿੱਚ ਇੱਕ ਘੱਟੋ-ਘੱਟ ਉਦਾਹਰਨ ਹੈ:
BEGIN;
-- reads that inform your decision
SELECT balance FROM accounts WHERE id = 42 FOR UPDATE;
-- writes that must stay together
UPDATE accounts SET balance = balance - 50 WHERE id = 42;
INSERT INTO ledger(account_id, amount, note) VALUES (42, -50, 'Purchase');
COMMIT;
-- on error (in code), run:
-- ROLLBACK;
ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਚਲਦੇ ਸਮੇਂ locks ਰੱਖਦੇ ਹਨ। ਜਿੰਨਾ ਲੰਮਾ ਤੁਸੀਂ ਉਨ੍ਹਾਂ ਨੂੰ ਖੁੱਲ੍ਹੇ ਰੱਖੋਗੇ, ਉਨ੍ਹਾਂ ਨੇ ਹੋਰ ਕੰਮ ਬਲੋਕ ਹੋਵੇਗਾ ਅਤੇ timeout ਜਾਂ deadlocks ਹੋਣ ਦੀ ਸੰਭਾਵਨਾ ਵਧੇਗੀ। ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਵਿੱਚ ਸਿਰਫ਼ ਜ਼ਰੂਰੀ ਕੰਮ ਰੱਖੋ, ਅਤੇ ধੀਰੇ ਕੰਮ (email ਭੇਜਣਾ, payment providers ਨੂੰ ਕਾਲ ਕਰਨ, PDF ਬਣਾਉਣਾ) ਬਾਹਰ ਲਿਜਾਓ।
ਜਦੋਂ ਕੁਝ fail ਹੋਵੇ, ਕਾਫੀ ਸੰਦਰਭ ਲਾਗ ਕਰੋ ਤਾਂ ਕਿ ਸਮੱਸਿਆ ਦੁਹਰਾਈ ਜਾ ਸਕੇ ਪਰ ਸੰਵੇਦਨਸ਼ੀਲ ਡੇਟਾ ਲੀਕ ਨਾ ਹੋਵੇ: ਵਰਕਫਲੋ ਨਾਂ, order_id ਜਾਂ user_id, ਮੁੱਖ ਪੈਰਾਮੀਟਰ (amount, currency), ਅਤੇ Postgres error code। Poora payload, card data ਜਾਂ ਨਿੱਜੀ ਵੇਰਵਾ ਨਾ ਲਾਗ ਕਰੋ।
Concurrency ਸਿਰਫ਼ ਇੱਕੋ ਸਮੇਂ ਦੋ ਚੀਜ਼ਾਂ ਹੋਣ ਹਨ। ਸੋਚੋ ਦੋ ਗਾਹਕ ਆਖਰੀ concert ticket ਖਰੀਦਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰ ਰਹੇ ਹਨ। ਦੋਵੇਂ ਸਕਰੀਨ '1 left' ਦਿਖਦੇ, ਦੋਵੇਂ Pay 'ਤੇ ਕਲਿਕ ਕਰਦੇ — ਹੁਣ ਤੁਹਾਡੀ ਐਪ ਨਿਰਣੇ ਕਰੇਗੀ ਕਿ ਕੌਣ ਨੂੰ ਮਿਲੇ।
ਬਿਨਾਂ ਸਰੱਖਿਆ ਦੇ, ਦੋਨੋਂ request ਇੱਕੋ ਹੀ ਪੁਰਾਣੀ value ਨੂੰ ਪੜ੍ਹ ਸਕਦੇ ਹਨ ਅਤੇ ਦੋਵੇਂ ਅਪਡੇਟ ਲਿਖ ਸਕਦੇ ਹਨ। ਇਸ ਤੋਂ negative inventory, duplicated reservations, ਜਾਂ payment ਬਗੈਰ order ਬਣ ਸਕਦਾ ਹੈ।
Row locks ਸਭ ਤੋਂ ਸਧਾਰਨ ਰੱਖਿਆ ਹਨ। ਤੁਸੀਂ ਉਸ ਖਾਸ ਰੋ ਨੂੰ ਲੌਕ ਕਰੋ ਜੋ ਤੁਸੀਂ ਬਦਲਣ ਜਾ ਰਹੇ ਹੋ, ਆਪਣੇ checks ਕਰੋ, ਤੇ ਫਿਰ update ਕਰੋ। ਹੋਰ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨਾਂ ਨੂੰ ਜੋ ਇੱਕੋ ਰੋ ਨੂੰ ਛੂਹਦੇ ਹਨ ਤੁਹਾਡੇ commit ਜਾਂ rollback ਤੱਕ ਰੁਕਣਾ ਪਵੇਗਾ, ਜੋ double updates ਨੂੰ ਰੋਕਦਾ ਹੈ।
ਇਕ ਆਮ ਪੈਟਰਨ: ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਸ਼ੁਰੂ ਕਰੋ, inventory ਰੋ ਨੂੰ FOR UPDATE ਨਾਲ select ਕਰੋ, ਯਕੀਨੀ ਬਣਾਓ ਕਿ stock ਹੈ, ਉਸਨੂੰ ਘਟਾਓ, ਫਿਰ order insert ਕਰੋ। ਇਹ 'ਦਰਵਾਜ਼ਾ ਰੋਕਦਾ' ਹੈ ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਨੁਕਤੀਮੈਤੇ ਕਦਮ ਮੁਕੰਮਲ ਕਰ ਲੈਂਦੇ ਹੋ।
ਆਈਸੋਲੇਸ਼ਨ ਲੈਵਲ ਨਿਰਧਾਰਤ ਕਰਦੇ ਹਨ ਕਿ concurrent ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨਾਂ ਤੋਂ ਤੁਸੀਂ ਕਿੰਨੀ ਓਵਰਲੈਪ ਅਸਮਾਨਤਾ ਬਰਦਾਸ਼ਤ ਕਰਦੇ ਹੋ। ਟਰੇਡ-ਆਫ ਆਮ ਤੌਰ 'ਤੇ ਸੁਰੱਖਿਆ ਬਨਾਮ ਗਤੀ ਹੁੰਦਾ ਹੈ:
ਲੌਕ ਛੋਟੇ ਰੱਖੋ। ਜੇ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਬਾਹਰ API ਨੂੰ ਕਾਲ ਕਰਦਿਆਂ ਜਾਂ ਯੂਜ਼ਰ ਕਾਰਵਾਈ ਦੀ ਉਡੀਕ ਕਰਦਿਆਂ ਖੁੱਲ੍ਹੀ ਰਹਿੰਦੀ ਹੈ, ਤਾਂ ਤੁਸੀਂ ਲੰਬੇ ਇੰਤਜ਼ਾਰ ਅਤੇ timeouts ਬਣਾਉਂਦੇ ਹੋ। ਇੱਕ ਸਾਫ਼ failure path ਨੂੰ ਤਰਜੀਹ ਦਿਓ: lock timeout ਸੈੱਟ ਕਰੋ, error ਨੂੰ catch ਕਰੋ, ਅਤੇ requests ਨੂੰ 'ਕਿਰਪਾ ਕਰਕੇ ਮੁੜ ਕੋਸ਼ਿਸ਼ ਕਰੋ' ਦੱਸੋ ਬਜਾਏ ਦੀਵੇਂ ਉਹਨਾਂ ਨੂੰ ਟੰਗੇ ਰਹਿਣ ਦੇ।
ਜੇ ਤੁਹਾਨੂੰ ਬਾਹਰਲੇ ਕੰਮ (ਜਿਵੇਂ card charge) ਕਰਨ ਦੀ ਲੋੜ ਹੈ, ਤਾਂ ਵਰਕਫਲੋ ਨੂੰ ਵੰਡੋ: ਤੇਜ਼ੀ ਨਾਲ reserve ਕਰੋ, commit ਕਰੋ, ਫਿਰ slow ਹਿੱਸਾ ਕਰੋ, ਅਤੇ ਫਿਰ ਇੱਕ ਹੋਰ ਛੋਟੇ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਨਾਲ finalize ਕਰੋ।
ਰੀਟ੍ਰਾਈਜ਼ Postgres-ਅਧਾਰਿਤ ਐਪਾਂ ਵਿੱਚ ਆਮ ਗੱਲ ਹੈ। ਇੱਕ request fail ਹੋ ਸਕਦੀ ਹੈ ਭਲਕੇ ਤੁਹਾਡੀ ਕੋਡ ਸਹੀ ਹੋਵੇ: deadlocks, statement timeouts, ਨੈੱਟਵਰਕ ਡ੍ਰਾਪਸ, ਜਾਂ ਉੱਚ ਆਈਸੋਲੇਸ਼ਨ ਹਾਲਤ 'ਚ serialization error। ਜੇ ਤੁਸੀਂ ਓਹੀ handler ਮੁੜ ਚਲਾਉਂਦੇ ਹੋ, ਤਾਂ ਤੁਸੀਂ ਦੂਜਾ order ਬਣਾਉਣਾ, ਦੋ ਵਾਰੀ charge ਕਰਣਾ, ਜਾਂ duplicate 'event' rows ਪੈਦਾ ਕਰ ਸਕਦੇ ਹੋ।
ਸੁਧਾਰ idempotency ਹੈ: operation ਨੂੰ ਇੱਕੋ input ਨਾਲ ਦੋ ਵਾਰੀ ਚਲਾਉਣਾ ਸੁਰੱਖਿਅਤ ਹੋਵੇ। ਡੇਟਾਬੇਸ ਨੂੰ ਪਛਾਣ ਕਰਨ ਯੋਗ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ ਕਿ 'ਇਹੋ ਹੀ request' ਹੈ ਅਤੇ ਇਕੋ ਤਰ੍ਹਾਂ ਜਵਾਬ ਦੇਵੇ।
ਇੱਕ ਪ੍ਰਯੋਗਿਕ ਪੈਟਰਨ: ਹਰ ਮਲਟੀ-ਸਟੈਪ ਵਰਕਫਲੋ ਨਾਲ ਇਕ idempotency key (ਅਕਸਰ client-generated request_id) ਜੋੜੋ ਅਤੇ ਉਸਨੂੰ main record 'ਤੇ ਸਟੋਰ ਕਰੋ, ਫਿਰ ਉਸ key 'ਤੇ unique constraint ਲਗਾਓ।
ਉਦਾਹਰਨ: checkout ਵਿੱਚ, user ਜਦੋਂ Pay 'ਤੇ ਕਲਿੱਕ ਕਰਦਾ ਹੈ request_id ਬਣਾਓ, ਫਿਰ order ਨੂੰ ਉਸ request_id ਨਾਲ insert ਕਰੋ। ਜੇ retry ਹੋਵੇ, ਦੂਜੀ ਕੋਸ਼ਿਸ਼ unique constraint ਨੂੰ ਹਿੱਟ ਕਰੇਗੀ ਅਤੇ ਤੁਸੀਂ ਮੌਜੂਦਾ order ਵਾਪਸ ਕਰੋਗੇ ਨਾਂ ਕਿ ਨਵਾਂ ਬਣਾਓਗੇ।
ਜੋ ਗੱਲਾਂ ਆਮ ਤੌਰ 'ਤੇ ਮਾਇਨੇ ਰੱਖਦੀਆਂ ਹਨ:
ਰਿਟ੍ਰਾਈ ਲੂਪ ਨੂੰ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਦੇ ਬਾਹਰ ਰੱਖੋ। ਹਰ ਕੋਸ਼ਿਸ਼ ਨਵੀਂ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਨਾਲ ਸ਼ੁਰੂ ਹੋਣੀ ਚਾਹੀਦੀ ਹੈ ਅਤੇ ਸਿਰੇ ਤੋਂ ਸਾਰਾ ਕੰਮ ਦੁਬਾਰਾ ਚਲਾਇਆ ਜਾਣਾ ਚਾਹੀਦਾ ਹੈ। ਇੱਕ failed transaction ਦੇ ਅੰਦਰ retry ਕਰਨ ਨਾਲ ਫਾਇਦਾ ਨਹੀਂ ਕਿਉਂਕਿ Postgres ਉਸਨੂੰ aborted ਮਾਰਕ ਕਰ ਚੁੱਕਾ ਹੁੰਦਾ ਹੈ।
ਛੋਟਾ ਉਦਾਹਰਨ: ਤੁਹਾਡੀ ਐਪ order ਬਣਾਉਣ ਅਤੇ inventory ਰਿਜ਼ਰਵ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰਦੀ ਹੈ, ਪਰ COMMIT ਦੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਬਾਅਦ timeout ਹੋ ਜਾਂਦੀ ਹੈ। client retry ਕਰਦਾ ਹੈ। idempotency key ਨਾਲ, ਦੂਜੀ request ਮੌਜੂਦਾ order ਨੂੰ ਵਾਪਸ ਕਰੇਗੀ ਅਤੇ ਦੂਜੀ reservation ਨੂੰ ਨਹੀਂ ਬਣਾਏਗੀ — ਨਤੀਜੇ ਦੁੱਗਨੇ ਨਹੀਂ ਹੋਵੇਂਗੇ।
ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਮਲਟੀ-ਸਟੈਪ ਵਰਕਫਲੋ ਨੂੰ ਇਕਠਾ ਰੱਖਦੇ ਹਨ, ਪਰ ਉਹ ਆਟੋਮੈਟਿਕ ਤੌਰ 'ਤੇ ਡੇਟਾ ਨੂੰ ਸਹੀ ਨਹੀਂ ਬਣਾਉਂਦੇ। ਮਜ਼ਬੂਤ ਤਰੀਕਾ ਇਹ ਹੈ ਕਿ ਡੇਟਾਬੇਸ ਵਿੱਚ 'ਗਲਤ' ਸਟੇਟਾਂ ਨੂੰ ਮਹਲਕ ਬਣਾਇਆ ਜਾਵੇ ਤਾਂ ਕਿ ਜੇ ਐਪ ਵਿੱਚ ਬੱਗ ਆਇਆ ਵੀ, ਨੁਕਸਾਨ ਘੱਟ ਰਹੇ।
ਸਿਰਫ਼ ਬੇਸਿਕ ਸੇਫਟੀ ਰੇਲ ਸ਼ੁਰੂ ਕਰੋ। Foreign keys ਯਕੀਨੀ ਬਣਾਉਂਦੇ ਹਨ ਕਿ referrals ਸahi ਹਨ (order line ਇੱਕ ਗਾਇਬ order ਨੂੰ point ਨਹੀਂ ਕਰ ਸਕਦੀ)। NOT NULL ਅੱਧ-ਭਰੇ rows ਰੋਕਦਾ ਹੈ। CHECK constraints ਉਹ ਮੁੱਲ ਫਿਰਦੇ ਹਨ ਜੋ ਮਾਨਸਕ ਨਹੀਂ ਹੁੰਦੇ (ਉਦਾਹਰਨ ਲਈ, quantity > 0, total_cents >= 0)। ਇਹ ਨਿਯਮ ਹਰ ਲਿਖਤ 'ਤੇ ਚੱਲਦੇ ਹਨ, ਚਾਹੇ ਕੋਈ ਵੀ service ਜਾਂ script ਡੇਟਾਬੇਸ ਨੂੰ ਛੂਹੇ।
ਲੰਮੇ ਵਰਕਫਲੋ ਲਈ, state changes ਨੂੰ ਖੁੱਲ੍ਹ ਕੇ ਮਾਡਲ ਕਰੋ। ਕਈ boolean ਫਲੈਗਾਂ ਦੀ ਬਜਾਏ ਇੱਕ status column (pending, paid, shipped, canceled) ਵਰਤੋ ਅਤੇ ਕੇਵਲ ਵੈਧ transitions ਦੀ ਆਗਿਆ ਦਿਓ। ਤੁਸੀਂ constraints ਜਾਂ triggers ਨਾਲ enforce ਕਰ ਸਕਦੇ ਹੋ ਤਾਂ ਕਿ database illegal jumps (ਜਿਵੇਂ shipped -> pending) ਨੂੰ ਮਨਾਏ।
Uniqueness correctness ਦੀ ਹੋਰ ਇੱਕ ਰੂਪ ਹੈ। ਜਿੱਥੇ duplicates ਤੁਹਾਡੇ ਵਰਕਫਲੋ ਨੂੰ ਤਬਾਹ ਕਰ ਸਕਦੇ ਹਨ, ਉਥੇ unique constraints ਲਗਾਓ: order_number, invoice_number, ਜਾਂ idempotency_key। ਫਿਰ ਜੇ ਤੁਹਾਡੇ app ਨੇ ਇੱਕੋ request ਨੂੰ retry ਕੀਤਾ, Postgres ਦੂਜੀ insert ਨੂੰ ਰੋਕੇਗਾ ਅਤੇ ਤੁਸੀਂ 'already processed' ਵਾਪਸ ਦੇ ਸਕਦੇ ਹੋ।
ਜੇ ਤੁਹਾਨੂੰ traceability ਚਾਹੀਦੀ ਹੈ, ਤਾਂ ਉਸਨੂੰ ਸਪਸ਼ਟ ਤੌਰ 'ਤੇ ਸਟੋਰੇ ਕਰੋ। ਇੱਕ audit table (ਜਾਂ history table) ਜੋ ਦਰਜ ਕਰਦਾ ਹੈ ਕਿ ਕਿਸਨੇ ਕੀ बदਲਿਆ ਅਤੇ ਕਦੋਂ, incidents ਦੌਰਾਨ 'mystery updates' ਨੂੰ ਤੱਥਾਂ ਵਿੱਚ ਬਦਲ ਦਿੰਦਾ ਹੈ।
ਅਧਿਕਤਰ ਅਧੂਰੀ ਲਿਖਤ 'ਬੁਰੀ SQL' ਕਾਰਨ ਨਹੀਂ ਹੁੰਦੀ। ਇਹ ਉਹਨਾਂ ਵਰਕਫਲੋ ਫੈਸਲਿਆਂ ਤੋਂ ਆਉਂਦੀ ਹੈ ਜੋ ਅਧੂਰੀ ਕਹਾਣੀ commit ਕਰਨਾ ਆਸਾਨ ਬਣਾਉਂਦੇ ਹਨ।
accounts ਫਿਰ orders update ਕਰਦਾ ਹੈ ਪਰ ਦੂਜਾ orders ਫਿਰ accounts, ਤਾਂ ਲੋਡ ਤੇ deadlocks ਦਾ ਖਤਰਾ ਵਧ ਜਾਂਦਾ ਹੈ।ਇੱਕ ਵਿਸ਼ੇਸ਼ ਉਦਾਹਰਣ: checkout ਵਿੱਚ ਤੁਸੀਂ inventory ਰਿਜ਼ਰਵ ਕਰਦੇ ਹੋ, order ਬਣਾਉਂਦੇ ਹੋ, ਅਤੇ ਫਿਰ card charge ਕਰਦੇ ਹੋ। ਜੇ ਤੁਸੀਂ card charge ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਦੇ ਅੰਦਰ ਕਰਦੇ ਹੋ, ਤਾਂ ਤੁਸੀਂ inventory lock ਹੋਈ ਹਾਲਤ ਵਿੱਚ network ਉੱਤੇ ਉਡੀਕ ਕਰ ਰਹੇ ਹੋ ਸਕਦੇ ਹੋ। ਜੇ charge ਸਫਲ ਹੋ ਜਾਂਦਾ ਪਰ ਬਾਅਦ ਵਿੱਚ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ rollback ਹੋ ਜਾਂਦੀ, ਤਾਂ ਤੁਸੀਂ ਗਾਹਕ ਨੂੰ charge ਕਰ ਚੁੱਕੇ ਹੋ ਪਰ ਕੋਈ order ਨਹੀਂ ਬਣਿਆ।
ਇੱਕ ਸੁਰੱਖਿਅਤ ਪੈਟਰਨ ਹੈ: ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਨੂੰ ਡੇਟਾਬੇਸ ਸਥਿਤੀ 'ਤੇ ਕੇਂਦਰਿਤ ਰੱਖੋ (reserve inventory, create order, record payment pending), commit ਕਰੋ, ਫਿਰ external API ਨੂੰ ਕਾਲ ਕਰੋ, ਅਤੇ ਨਤੀਜਾ ਨੂੰ ਇੱਕ ਨਵੇਂ ਛੋਟੇ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਵਿੱਚ ਲਿਖੋ। ਕਈ ਟੀਮਾਂ ਇਸਨੂੰ pending status ਅਤੇ background job ਨਾਲ ਅਮਲ ਕਰਦੀਆਂ ਹਨ।
ਜਦੋਂ ਕਿਸੇ ਵਰਕਫਲੋ ਵਿੱਚ ਕਈ ਕਦਮ ਹੁੰਦੇ ਹਨ (insert, update, charge, send), ਮਕਸਦ ਸਧਾਰਨ ਹੈ: ਜਾਂ ਤਾਂ ਸਭ ਕੁਝ ਦਰਜ ਹੋਵੇ, ਜਾਂ ਕੁਛ ਵੀ ਨਾ ਹੋਵੇ।
ਹਰ ਲਾਜ਼ਮੀ ਡੇਟਾਬੇਸ ਲਿਖਤ ਨੂੰ ਇੱਕ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਵਿੱਚ ਰੱਖੋ। ਜੇ ਇੱਕ ਕਦਮ fail ਹੋਵੇ, rollback ਕਰੋ ਅਤੇ ਡੇਟਾ ਬਿਲਕੁਲ ਉਹੀ ਰਹਿਣ ਦਿਓ ਜੋ ਪਹਿਲਾਂ ਸੀ।
ਸਫਲਤਾ ਦੀ ਸ਼ਰਤ ਸਪਸ਼ਟ ਬਣਾਓ। ਉਦਾਹਰਨ: 'Order ਬਣਿਆ, stock reservard ਹੋਇਆ, ਅਤੇ payment status ਦਰਜ ਹੋਇਆ'। ਇਸ ਤੋਂ ਘੱਟ ਕੁਛ ਵੀ failure ਮਾਰਗ ਹੈ ਜੋ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ abort ਕਰੇਗਾ।
BEGIN ... COMMIT ਬਲਾਕ ਵਿੱਚ ਹੋਣ।ROLLBACK, ਅਤੇ caller ਨੂੰ ਇਕ ਸਪਸ਼ਟ failure ਨਤੀਜਾ ਮਿਲੇ।ਉਮੀਦ ਕਰੋ ਉਹੀ request ਮੁੜ ਕੀਤੀ ਜਾ ਸਕਦੀ ਹੈ। ਡੇਟਾਬੇਸ ਤੁਹਾਡੀ ਮਦਦ ਕਰੇ ਤਾਂ ਕਿ only-once ਨਿਯਮ ਲਾਗੂ ਹੋਣ।
ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਵਿੱਚ ਘੱਟੋ-ਘੱਟ ਕੰਮ ਕਰੋ, ਅਤੇ locks ਰੱਖਦੇ ਹੋਏ network calls ਤੋਂ ਬਚੋ।
ਜੇ ਤੁਸੀਂ ਨਹੀਂ ਵੇਖ ਸਕਦੇ ਕਿ ਇਹ ਕਿੱਥੇ ਟੁੱਟਦਾ ਹੈ, ਤਾਂ ਤੁਸੀਂ ਅਨੁਮਾਨ ਲਗਾਉਂਦੇ ਰਹੋਗੇ।
ਇੱਕ checkout ਵਿੱਚ ਕਈ ਕਦਮ ਹਨ ਜੋ ਇਕੱਠੇ ਹੋਏ ਜਾਣੇ ਚਾਹੀਦੇ ਹਨ: order ਬਣਾਉਣਾ, inventory ਰਿਜ਼ਰਵ ਕਰਨਾ, payment attempt ਦਰਜ ਕਰਨਾ, ਫਿਰ order status ਮਾਰਕ ਕਰਨਾ।
ਕਲਪਨਾ ਕਰੋ ਇੱਕ ਯੂਜ਼ਰ 1 ਆਈਟਮ ਲਈ Buy 'ਤੇ ਕਲਿੱਕ ਕਰਦਾ ਹੈ।
ਇੱਕ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਦੇ ਅੰਦਰ, ਸਿਰਫ਼ ਡੇਟਾਬੇਸ ਬਦਲਾਅ ਕਰੋ:
orders ਰੋ insert ਕਰੋ ਜਿਸਦਾ status pending_payment ਹੋਵੇ।inventory.available ਘਟਾਓ ਜਾਂ reservations ਰੋ ਬਣਾਓ)।idempotency_key ਦੇ ਨਾਲ payment_intents ਰੋ insert ਕਰੋ (unique)।outbox ਰੋ insert ਕਰੋ ਜਿਵੇਂ 'order_created'।ਜੇ ਕੋਈ ਵੀ ਬਿਆਨ fail ਹੁੰਦਾ (out of stock, constraint error, crash), Postgres ਪੂਰੇ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਨੂੰ rollback ਕਰ ਦੇਵੇਗਾ। ਤੁਹਾਡੇ ਕੋਲ order ਬਿਨਾਂ reservation ਵਾਲਾ scenario ਜਾਂ reservation ਬਿਨਾਂ order ਦਾ scenario ਨਹੀਂ ਬਚੇਗਾ।
Payment provider ਤੁਹਾਡੇ ਡੇਟਾਬੇਸ ਤੋਂ ਬਾਹਰ ਹੈ, ਇਸ ਲਈ ਇਸਨੂੰ ਵੱਖਰਾ ਕਦਮ ਸਮਝੋ।
ਜੇ provider call commit ਤੋਂ ਪਹਿਲਾਂ fail ਹੋਵੇ, ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ abort ਕਰੋ ਅਤੇ ਕੁਛ ਵੀ ਲਿਖਿਆ ਨਾ ਜਾਵੇ। ਜੇ provider call commit ਤੋਂ ਬਾਅਦ fail ਹੋਵੇ, ਤਾਂ ਇੱਕ ਨਵੀਂ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਚਲਾ ਕੇ payment attempt ਨੂੰ failed ਮਾਰਕ ਕਰੋ, reservation ਰਿਲੀਜ਼ ਕਰੋ, ਅਤੇ order status ਨੂੰ canceled ਕਰੋ।
ਕਲਾਇੰਟ ਨੂੰ ਹਰ checkout ਕੋਸ਼ਿਸ਼ ਲਈ ਇੱਕ idempotency_key ਭੇਜਣ ਦਿਓ। ਇਸਨੂੰ payment_intents(idempotency_key) 'ਤੇ unique index ਨਾਲ enforce ਕਰੋ (ਜਾਂ orders 'ਤੇ ਜੇ ਤੁਸੀਂ ਚਾਹੋ)। ਜਦੋਂ retry ਹੋਵੇ, ਤੁਹਾਡੀ ਕੋਡ ਮੌਜੂਦਾ rows ਨੂੰ ਲੱਭੇਗੀ ਅਤੇ insert ਕਰਨ ਦੀ ਬਜਾਏ ਅੱਗੇ ਵਧੇਗੀ।
ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਦੇ ਅੰਦਰ email ਨਾ ਭੇਜੋ। ਇਕ outbox record ਉਹੀ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਵਿੱਚ ਲਿਖੋ, ਫਿਰ commit ਤੋਂ ਬਾਅਦ background worker email ਭੇਜੇ। ਇਸ ਤਰ੍ਹਾਂ ਤੁਸੀਂ ਕਦੇ ਵੀ ਐਸਾ email ਨਹੀਂ ਭੇਜੋਗੇ ਜੋ ਉਸ order ਲਈ rollback ਹੋ ਗਿਆ।
ਇੱਕ workflow ਚੁਣੋ ਜੋ ਇੱਕ ਤੋਂ ਵੱਧ ਟੇਬਲ ਛੁਹਦਾ ਹੈ: signup + welcome email enqueue, checkout + inventory, invoice + ledger entry, ਜਾਂ create project + default settings।
ਪਹਿਲਾਂ ਕਦਮ ਲਿਖੋ, ਫਿਰ ਉਹ ਨਿਯਮ ਜੋ ਹਮੇਸ਼ਾ ਸੱਚ ਹੋਣ (ਤੁਹਾਡੀਆਂ invariants) ਨਿਰਧਾਰਤ ਕਰੋ। ਉਦਾਹਰਨ: 'ਇੱਕ order ਜਾਂ ਤਾਂ ਪੂਰੀ ਤਰ੍ਹਾਂ paid ਅਤੇ reserved ਹੈ, ਜਾਂ ਨਾ ਹੀ paid ਅਤੇ ਨਾ reserved। ਕਦੇ ਆਧਾ-reserved ਨਹੀਂ।' ਇਨ੍ਹਾਂ ਨਿਯਮਾਂ ਨੂੰ ਇੱਕ all-or-nothing ਯੂਨਿਟ ਵਿੱਚ ਬਦਲੋ।
ਇੱਕ ਸਧਾਰਨ ਯੋਜਨਾ:
ਫਿਰ ਮਨ-ਮੰਦਾ ਹਾਲਤਾਂ ਨੂੰ ਜਾਣ-ਬੂझ ਕੇ ਟੈਸਟ ਕਰੋ। step 2 ਤੋਂ ਬਾਅਦ crash simulate ਕਰੋ, commit ਤੋਂ ਠੀਕ ਪਹਿਲਾਂ timeout simulate ਕਰੋ, ਅਤੇ UI ਤੋਂ double-submit simulate ਕਰੋ। ਟੀਚਾ ਬੋਰਿੰਗ ਨਤੀਜੇ ਹਨ: ਕੋਈ orphan rows ਨਹੀਂ, ਕੋਈ double charges ਨਹੀਂ, ਕੋਈ forever pending ਨਹੀਂ।
ਜੇ ਤੁਸੀਂ ਤੇਜ਼ੀ ਨਾਲ prototype ਬਣਾ ਰਹੇ ਹੋ, ਤਾਂ ਵਰਕਫਲੋ ਨੂੰ ਪਹਿਲਾਂ ਇੱਕ planning-ਛੇਤੀ ਟੂਲ ਵਿੱਚ ਸਕੈਚ ਕਰਨਾ ਮਦਦਗਾਰ ਹੁੰਦਾ ਹੈ। ਉਦਾਹਰਨ ਲਈ, Koder.ai (koder.ai) ਦੇ ਕੋਲ Planning Mode ਹੈ ਅਤੇ snapshots ਤੇ rollback ਨੂੰ support ਕਰਦਾ ਹੈ, ਜੋ ਕਿ ਟ੍ਰਾਂਜ਼ੈਕਸ਼ਨ ਸਰਹੱਦ ਅਤੇ constraints 'ਤੇ iterate ਕਰਦਿਆਂ ਮਦਦਗਾਰ ਹੋ ਸਕਦਾ ਹੈ।
ਇਸ ਹਫ਼ਤੇ ਇੱਕ ਵਰਕਫਲੋ ਲਈ ਇਹ ਕਰੋ। ਦੂਜਾ ਇੱਕ ਕਾਫ਼ੀ ਤੇਜ਼ ਹੋਵੇਗਾ।