0084

Blobs Are Billed Separately

Performance
Easy
blob
calcfields
archive
sql-budget

Blobs Are Billed Separately

Operations archives every outbound document into a custom table, and each entry keeps its payload in a Content Blob field. Newer entries also record the payload's size in bytes at archive time; entries archived before that column existed carry 0 there, and their size can only be learned by measuring the stored payload itself. Finance wants a storage total per department — and the first attempt made the DBA flinch: the archive holds hundreds of documents, and the job dragged every single payload across the wire to add up a column of numbers it mostly already had. A Blob is never fetched with its row; every payload you ask for is billed as its own trip to the database, so the audit must ask only for the payloads it genuinely needs.

Requirements

Keep the table "Archived Document" exactly as shipped in the starter — the tests seed and read it by that name. Pick object IDs in the range 50100–50199 and reference other objects by name, never by ID.

The starter also ships a codeunit "Archive Payload Audit" with one public procedure — keep both names and this exact signature:

procedure TotalPayloadBytes(var ArchivedDocument: Record "Archived Document"; DepartmentCode: Code[20]): Integer

Rules:

  1. Consider exactly the archived documents whose "Department Code" equals DepartmentCode — the record instance arrives with no filters set and nothing read into it, so narrowing down to the department is the procedure's job.
  2. Each document contributes its "Recorded Size" when that field is greater than 0; otherwise it contributes the byte count of its stored Content payload (an empty payload contributes 0). Return the sum. A recorded size is authoritative even when the stored payload happens to differ — the tests seed such disagreements on purpose.
  3. A department with no archived documents returns 0; the procedure never raises an error.
  4. Do the whole pass on the very record instance you were handed — the tests inspect it afterwards, so don't swap the work onto a copy. After the call returns, the instance must rest on the department's last archive entry (the highest "Entry No." inside the department).
  5. The statement budget: one call over a department of 35–45 documents, roughly a third of them legacy, must execute at most 25 SQL statements. Grading measures SessionInformation.SqlStatementsExecuted around a single call — an implementation that goes back to the database for every document's payload spends one statement per document and fails.
  6. The row budget: the same call must read at most 120 rows, measured with SessionInformation.SqlRowsRead, while the archive table holds about 600 documents — most of them belonging to other departments. An implementation that scans the whole archive and picks the department in code reads 600+ rows and fails, whatever its total says.

Payload discipline — fetching a payload only for the documents that need measuring, never hauling every payload along with its row — is the point of the exercise, but it is not graded on its own: the grading session can count SQL statements and rows, not the bytes that crossed the wire, so only the two budgets above and the arithmetic judge your choice.

What the tests check

The tests seed "Archived Document" entries with department codes, recorded sizes and payload lengths generated fresh every run, so hardcoded totals fail; recorded sizes are chosen to disagree with the actual stored payloads, so an implementation that measures everything "to be safe" fails on arithmetic, not just on cost. Decoy documents in other departments must stay out of the total. Rule 4 is asserted directly on the record instance after a call: its "Entry No." must be the department's last entry. The two budget tests warm the caches with one throwaway call, then invalidate the server's data cache with a decoy write — a repeated call served from cache memory costs zero SQL, so cached reads can't smuggle payload traffic past the budget — and snapshot the SessionInformation counters around a second call, asserting the total is correct before judging the cost.

Learn More

Hint 1
Start from what each document already tells you: a filled-in Recorded Size is the final answer for that document, no payload required. Work out how many of the department's documents genuinely need their stored payload opened — every complaint the grader raises traces back to payloads fetched for documents that had already answered.
Hint 2
A Blob never travels with its row: after FindSet, Content is empty until you explicitly fetch it, and each fetch is its own SQL statement against one row. Narrow the record to the department first — SetRange, then one FindSet pass — and pay the fetch price only inside the branch where Recorded Size is 0. CalcFields(Content) is that fetch, and Content.Length is the byte count.
Hint 3
SetAutoCalcFields(Content) looks like the tidy fix: one statement, and every payload arrives with its row. Here it is the trap — it drags payloads across the wire for documents whose size was already on record, two thirds of the department, and the grader cannot see those bytes (it counts statements and rows), so nothing will tell you. Keep the plain filtered FindSet and call CalcFields(Content) only for the zero-Recorded-Size documents.
ALBusiness Central 28.4
Press Compile to check your code compiles — Submit runs the tests.
The code editor is desktop-only
Open this problem on a computer to write and run code. Reading the description, tests and discussion works fine here.