Question 181 of 169
PCAP Object-Oriented Programming Practice Question
You are working on a legacy system that processes financial transactions. The system uses a class hierarchy: Transaction (base), Deposit, Withdrawal, Transfer. Each subclass overrides a method 'process()' to handle its specific logic. The code often runs in a multi-threaded environment and you notice intermittent errors where a transaction is processed twice. The logging shows that the same transaction object is being passed to the process method multiple times. The transaction objects are created from a factory function that caches recently used transactions. The errors seem to occur when two threads call the factory at the same time with the same parameters. After investigating, you find that the factory uses a class-level dictionary to cache objects. Which of the following is the most appropriate solution to prevent double processing?
⚠ Common exam trap
Python Institute often tests the misconception that preventing object reuse or adding locks in the factory is sufficient to fix double processing, when the real requirement is to make the operation itself idempotent to handle any scenario where the same object is processed more than once.
Answer choices
Why each option matters
Answer the question above first, then reveal the full breakdown to understand why each option is right or wrong.
Correct answer & explanation
✓
Make the process() method idempotent by checking if the transaction has already been applied to the account (e.g., check balance changes)
The core issue is that the same transaction object can be processed multiple times in a multi-threaded environment, even if the factory is fixed. Making process() idempotent by checking whether the transaction has already been applied (e.g., verifying account balance changes) ensures that repeated calls with the same object do not cause duplicate financial effects, directly addressing the symptom of double processing regardless of how the object is cached or retrieved.
Answer analysis
Option-by-option breakdown
For each option: why learners choose it and why it is or isn't the right answer here.
- ✗
Add a lock around the cache lookup and creation in the factory function
Why it's wrong here
Adding a lock around the cache lookup and creation only synchronizes the factory method, not the processing path. If the same transaction object is retrieved from the cache and passed to process() by multiple threads, each thread will still execute the side effects (e.g., balance mutation) concurrently. The lock does not introduce idempotency, so duplicate accounting effects remain possible, while also adding unnecessary contention on the factory and leaving the core vulnerability unaddressed.
- ✗
Add a flag to each transaction object to indicate if it has been processed, and check it at the start of process()
Why it's wrong here
A boolean 'processed' flag introduces a classic check-then-act race: thread A checks the flag, thread B checks the flag, both see False, and both proceed to process the transaction. Even if the flag is marked before processing, a crash or exception after the flag is set can cause the transaction to be permanently skipped without applying its effect. Additionally, the flag is per-object and volatile in-memory state; it does not survive object reconstruction, restart, or duplication, and it does not protect against the same logical transaction being replayed as a different instance.
- ✗
Remove the caching mechanism from the factory function to ensure new objects are always created
Why it's wrong here
Removing the caching mechanism treats a symptom rather than the cause: the factory cache is not what makes process() non-idempotent. Even with a fresh object every time, the same transaction ID or payload can be submitted again, and each new object will re-apply the financial effect, causing duplicates. Furthermore, constant object creation can degrade performance, defeat interning or deduplication benefits, and leak resources if the transaction references external state, so this change adds risk without guaranteeing correctness.
- ✓
Make the process() method idempotent by checking if the transaction has already been applied to the account (e.g., check balance changes)
Why this is correct
Idempotency is the correct design because it makes each transaction carry a natural guard: before applying changes, process() can verify whether the transaction's effects are already reflected in the account (e.g., comparing a journal entry, version number, or resulting balance). In a multi-threaded environment, this check must be atomic with the application step—such as using a database transaction with a unique constraint on the transaction ID—so repeated calls from any thread, queue replay, or retry produce only one net effect. This approach is thread-safe, recoverable after crashes, and eliminates the need to force uniqueness at the object or call-site level.
About these practice questions
Courseiva creates original exam-style practice questions with explanations and wrong-answer analysis. It does not publish real exam questions, exam dumps, or protected exam content. Learn why practice questions differ from exam dumps →
Last reviewed: Jun 30, 2026
This PCAP practice question is part of Courseiva's free Python Institute certification practice question bank. Courseiva provides original exam-style practice questions with explanations, topic-based practice, mock exams, readiness tracking, and study analytics to help learners prepare for the PCAP exam.
Question Discussion
Share a tip, memory trick, or ask about the reasoning behind this question. Do not post real exam questions, leaked content, braindumps, or copyrighted exam material. Comments are moderated and may be removed without notice.
Sign in to join the discussion.