devinside.

CONTEMPORARY LABORATORY · ORIGINAL EVIDENCE

Following the Cost of One Task Through Azure

This notebook preserves the later investigation, its original measured results, exact versions and execution dates. It is separate from the historical subject scope of the reconstructed article.

Return to the historical explanation → · Current reconstruction sources

Contemporary laboratory record. Original provenance and measurement limits remain intact.

An application task crosses resources that were not purchased for that task alone. It uses part of a shared compute process, reads or writes storage, transfers data and may depend on capacity that remains allocated while no task is running. A trace can show the path of execution without directly telling us which part of a resource charge belongs to that path.

This article implements a small attribution model in C#. Its inputs are synthetic accounting units labelled SYN. The program runs locally, transforms four declared records and verifies its arithmetic. The observations concern that code, not the operating expenditure of a real Azure deployment.

The distinction between source data and assignment policy is the central technical point. A source amount describes a recorded quantity within a particular scope. A policy distributes that amount among tasks. The arithmetic can be exact while the choice of policy remains an assumption that needs review.

The fixture keeps those two things separate. It also preserves amounts that the current policy cannot assign, refuses incompatible periods or units, detects repeated records and resolves fractional residuals deterministically. Those are useful properties before a report is used to support a decision.

A trace and an attribution record describe different boundaries

A request trace can identify a task, attempts and participating components. A resource-level record can cover an interval and work belonging to many tasks. Connecting them requires an explicit rule. The existence of a trace identifier does not by itself supply the appropriate share of every shared resource.

For example, an application may reuse a database, storage container and compute instance across many operations. Assigning a fraction of their aggregate amount can be based on measured work, reserved capacity or a declared ownership policy. Each basis answers a different question. A report should identify which one it uses rather than present every assigned value as direct consumption.

The timing boundary matters as well. Task completion and source-data finalization need not occur at the same moment. A preliminary result can be useful, but its provisional terms must remain visible. Revising the source later should create a new identifiable result rather than silently rewriting an earlier decision.

Microsoft's public Cost Management documentation describes differences in data processing and availability. That guidance motivates preserving the source period, dataset and interpretation alongside a result. This laboratory uses only literal synthetic records and does not fetch a private dataset. Understand Cost Management data.

Separate records from the policy

01 / Watch the mechanism

An assignment is a policy, not direct consumption

The declared policy gives task-a117 and task-b45, while preserving8units outside attribution.
Enlarge diagram

Static view. Play to see how the mechanism unfolds.

The declared policy gives task-a117 and task-b45, while preserving8units outside attribution.

Interpretation and evidence
  • Keep source and policy separate.
  • Name the weights.
  • Do not disguise unresolved attribution.
Executed synthetic arithmetic fixture; not external financial data.

The synthetic input contains four lines: compute contributes 120 SYN units, storage 30, transfer 12 and an unmapped resource 8. Each line has an identity, resource label, period and unit code. The resource labels are laboratory categories, not quoted Azure meter names or service prices.

A separate policy assigns compute with weights three to task-a and one to task-b. Storage is split equally. Transfer belongs entirely to task-a. No assignment rule exists for the unmapped line, so its amount remains explicitly unallocated.

The literal expected totals are117for task-a and 45 for task-b, with 8 unallocated. Their sum is170, exactly the source total. These expectations are written independently of the routine that calculates the shares. A correct output means that the routine followed the declared policy; it does not establish that those weights are the right policy for a real organization.

That separation is what makes the example useful for engineering review. A reviewer can challenge the assignment rule without losing the original source records. They can also verify that an implementation change preserves the same policy, instead of interpreting a different distribution as a performance improvement.

Conservation is an acceptance condition

02 / A concrete example

Conservation keeps the source visible

The exact conservation check protects arithmetic integrity. It does not justify the selected weights.

The exact conservation check protects arithmetic integrity. It does not justify the selected weights.

Interpretation and evidence
  • Preserve the total.
  • Challenge the policy separately.
  • Known unallocated is not zero.
Executed synthetic arithmetic fixture; not external financial data.

The first invariant is that task allocations plus the unallocated amount equal the deduplicated source total. The routine checks this condition before returning its result. An allocation that silently loses or creates units cannot support a dependable interpretation of the input.

The fixture uses decimal amounts with at most two fractional digits. That is an application constraint for this example, not a claim about the precision of every Azure dataset or every currency. A real adapter must preserve its selected source schema and choose any reporting precision deliberately.

The result exposes the relevant quantities separately:

public sealed record Allocation(
    decimal SourceTotal,
    decimal Unallocated,
    SortedDictionary<string, decimal> ByTask);

A reader can verify the total without assuming every line has been assigned. That distinction matters because a complete source dataset can still have incomplete attribution. The routine does not repair the appearance of completeness by distributing unresolved data among the tasks it already knows.

Conservation is necessary but not sufficient. Many different weight policies can preserve the same total. The invariant catches arithmetic loss and duplication, while review of the policy determines whether the assignment is useful for the intended question. Treating those as separate forms of evidence prevents a correct sum from being mistaken for a justified economic model.

Rounding must not depend on iteration order

03 / A concrete example

A subunit needs an explicit destination

The fixture makes residual assignment deterministic instead of letting iteration order choose the result.

The fixture makes residual assignment deterministic instead of letting iteration order choose the result.

Interpretation and evidence
  • State output precision.
  • Preserve sign for adjustments.
  • Version the tie-break rule.
Executed synthetic arithmetic fixture; not external financial data.

A one-subunit amount split among three equal tasks cannot give each task an exact third if the output contract allows only whole subunits. The fixture handles this through a declared largest-remainder rule. It calculates proportional shares, takes their whole-subunit portions, then assigns the remaining subunits in descending remainder order. Ties use ordinal task identifiers.

This gives the same result when the policy is evaluated again. The rule does not depend on which dictionary entry happens to be visited first. The residual is part of the policy, not an accidental rounding artifact that the report hopes will disappear in a total.

For negative source amounts, the routine allocates the magnitude under the same rule and restores the sign. The selected negative fixture assigns minus 4 SYN units as minus 3 and minus 1 under the compute weights. It checks arithmetic behavior only; it does not claim that every real provider adjustment uses that representation.

The implementation uses decimal values to keep the chosen two-decimal input domain explicit. It also validates nonpositive weights and repeated task identifiers within one policy. An invalid allocation basis is refused before it produces a plausible-looking result.

A broader implementation might choose another residual rule, additional precision or a distinct treatment of adjustments. Those can be legitimate choices. They should have their own expectations and provenance rather than being hidden inside an implementation change advertised as optimization.

Duplicate input is not a new charge

04 / A concrete example

Repeated and conflicting lines are different

The internal record model detects repetition and conflict. It is not an adapter for every Azure export schema.

The internal record model detects repetition and conflict. It is not an adapter for every Azure export schema.

Interpretation and evidence
  • Preserve source identity.
  • Do not silently choose the latest bytes.
  • Test the actual ingestion schema separately.
Executed synthetic arithmetic fixture; not external financial data.

Each source line has an application-owned identity. An identical repeated line is counted once. A repeated identity with changed content is rejected as a conflict. The model therefore distinguishes replay from disagreement instead of choosing the last record it happened to receive.

A real export adapter would need to establish that identity according to its source's actual rules. A file name or row position is not automatically a stable business identity. If the source represents corrections as revised records, the adapter must retain enough version information to explain which revision was used.

The companion does not invent an Azure export schema. It works with a simplified internal record model so that the allocation mechanics are inspectable. Adapting a documented export would be a separate implementation step, with its own input validation and compatibility fixtures.

Microsoft's export guidance supports selecting dataset schema versions and describes export output behavior. That is why a versioned adapter belongs in a serious design: a stable downstream record should not conceal an unexamined change in upstream fields. No export job was created during this laboratory. Cost Management export documentation.

Keeping the source snapshot also matters after a result is published. Replacing the input file and running the same report name can silently change the basis of an earlier decision. Preserve the old result and identify the new source revision, even when the new data is more accurate.

An unresolved amount is different from zero

The unmapped 8 units remain visible as Unallocated. They are known input that the selected policy cannot assign. They are not a missing source observation, and they are not a known zero amount. A report should preserve those three states separately.

The unresolved resource could require an agreed allocation rule, better instrumentation or a different classification. The routine does not decide which explanation applies. It records the boundary of the current policy and keeps the unresolved value in the conservation equation.

This behavior protects against a subtle bias. If a report assigns shared amounts only to the tasks that happen to be observed, missing task observations can change the apparent distribution. The arithmetic can still sum correctly while the interpretation becomes misleading. The current fixture assumes its declared weights define the selected population; it does not solve incomplete telemetry.

A useful report can therefore say both that its source total is conserved and that some attribution remains unresolved. The two statements are compatible. Hiding the unresolved portion would make the result easier to read and harder to trust.

The next action should address the uncertainty that matters to the decision. If the unresolved portion is small and outside the chosen question, retaining it may be sufficient. If it could reverse the comparison, the team needs another observation or an agreed policy before treating the task totals as decision-ready.

Nine checks establish the selected contract

The accepted correctness run verifies the literal totals 117 and 45, source total 170 and unallocated 8. It checks that the task totals and unresolved portion exactly reconcile to the source. It then replays an identical line and requires the result to remain unchanged.

The negative cases reject changed content under the same identity, mixed unit codes, mixed periods and a zero allocation weight. These cases prevent incompatible inputs from becoming a single attractive total. The negative-adjustment fixture checks sign preservation, and the one-subunit fixture checks deterministic remainder handling.

All nine selected checks passed under SDK 10.0.400 and runtime 10.0.15. They are targeted acceptance cases, not an exhaustive proof over every decimal value or malformed object. The exact source inputs and expected results are supplied with the code.

The routine is intentionally straightforward. It creates intermediate collections and sorts remainders because those operations make the allocation policy visible. There is no zero-allocation claim. If this transformation becomes a measured hot path, an optimized implementation should be evaluated against the same identity, rounding and conservation expectations.

This is a recurring distinction in performance work: a slower but correct routine is a useful baseline. Removing checks or changing policy can make a loop faster, but it is not an optimization of the same promise unless the changed behavior is explicitly accepted.

The execution measurement is local arithmetic, not cloud expenditure

05 / A concrete example

Measured code cost is not resource expenditure

The timing concerns a four-line transformation on a shared developer host. It cannot be converted into a real cloud price without another justified model.

The timing concerns a four-line transformation on a shared developer host. It cannot be converted into a real cloud price without another justified model.

Interpretation and evidence
  • Keep raw samples.
  • Do not call allocation bytes a charge.
  • Separate implementation cost from attribution.
Executed synthetic arithmetic fixture; not external financial data.

The measured run repeats the four-line fixture 10000 times per sample, with seven samples in one process. Each call checks the expected allocation to task-a and adds the source total to an observable sink. The interval includes the local transformation and its managed allocations.

Sample Allocations performed Batch duration Managed bytes
1 10000 147.4447 ms 43047368 B
2 10000 110.0472 ms 43040040 B
3 10000 124.1455 ms 43040040 B
4 10000 126.7057 ms 43040040 B
5 10000 127.7357 ms 43040040 B
6 10000 77.7823 ms 43040040 B
7 10000 44.2716 ms 43040040 B

The median duration is 124.1455 ms per 10000 calls, with a wide range across samples. Later samples are much faster than the earliest ones. This is an exploratory process-level observation with evolving runtime state, not a stable throughput benchmark or a claim about Azure service latency.

Managed allocation is approximately 43 MB per 10000 calls in this implementation. That number describes the local code path. It is not a resource bill, a tariff or a conversion from bytes into money. No such conversion is attempted by the program.

The result can guide a technical follow-up if the routine's frequency justifies it. A more allocation-conscious implementation might avoid some intermediate collections while preserving the policy. But if attribution runs infrequently compared with the actual application workload, optimizing this function may not change any material operating decision.

The experiment also does not compare alternative allocation policies. Every timed call applies the same policy to the same literal input. It measures execution of an interpretation, not whether that interpretation is the most appropriate one.

Internal allocation does not rewrite the provider's invoice

A task-level number can acquire more authority than its method supports. Once a dashboard places an amount beside a task, readers may assume the provider charged precisely that amount for executing it. Shared-resource attribution often cannot justify that interpretation without additional conditions.

Microsoft's cost-allocation guidance distinguishes redistribution for internal accountability from changes to the billing invoice. The companion preserves that distinction: it assigns a declared source amount under a policy, rather than changing or certifying an external financial record. Azure cost-allocation documentation.

This matters when discussing an optimization. Reducing work on one task can change its assigned share while a shared resource remains provisioned. Conversely, a change to the provisioned arrangement can affect aggregate expenditure without changing the source code of an individual request. The engineering model must state which question its output answers.

The article does not choose a business accounting method. It shows how to preserve the observations and transformations so that the chosen interpretation can be reviewed. Technical exactness should make the business question clearer, not quietly answer another one.

A useful report therefore exposes the source scope, policy and unresolved portion alongside the total. A reader can then ask whether the assignment supports capacity planning, internal accountability or another specific decision. Those purposes may require different rules.

Connect resource attribution with task outcomes carefully

An AI operation may include retrieval, several model attempts, tool calls, validation and follow-up work. The agent-ledger laboratory shows how attempt identity and incomplete observations can be represented without counting the same event twice.

That ledger and this resource allocator answer different questions. Their totals should not simply be added, because they may describe overlapping work, different periods or different accounting views. An integration needs to identify which quantities are already represented and which remain shared.

The denominator also matters. An assigned amount per started task is not the same as an amount per accepted result. Work on rejected or incomplete tasks does not disappear merely because the final report counts only accepted outcomes. A combined model should preserve both the work performed and the rule defining acceptance.

The current companion does not implement that combined system. It provides a small allocation component with independent expected results. Its relationship to the task ledger is analytical and architectural, not a claim that a complete cloud cost-of-service pipeline has been deployed.

A productive next experiment would focus on the missing term that could change the decision. If shared indexing dominates uncertainty, establish its refresh pattern and a defensible allocation basis. If follow-up effort matters, observe the relevant process rather than inserting an invented constant. Precision in one visible term cannot compensate for an unexamined assumption elsewhere.

Keep the interpretation versioned with the source

A reproducible result needs an identity for the source snapshot, the normalized schema and the allocation policy. In this companion, the inputs and policy are literal fixtures preserved with the source revision. A real pipeline should make those identities explicit in its output.

When the policy changes, the result may change even if the source total does not. That is not necessarily a correction of bad data. It is a new interpretation. Retain the prior result and explain the difference so that earlier decisions can still be understood.

A source correction is different again. It can alter the amount being allocated, the period or the resource identity. The adapter needs to distinguish that revision from a duplicate file. Without that distinction, rerunning a report can either double-count the source or silently discard a legitimate correction.

The strict period and unit guards in this fixture keep those larger questions outside the routine. A broader system can support conversion or allocation across intervals, but it must preserve the conversion source, timing and rounding rules. It should not broaden the accepted domain merely because a string comparison is inconvenient.

The benefit of explicit contracts is that uncertainty remains addressable. An unknown input, an unresolved assignment and a known zero each lead to different next steps. A report that renders all three as zero may look complete while removing exactly the information needed to improve it.

Use the model without mistaking it for the whole system

The companion contains no Azure SDK calls. That is intentional: the selected experiment concerns attribution after source normalization. It cannot establish permissions, export delivery, service availability or compatibility with a particular organization's data. Those belong to an ingestion and deployment boundary that has not been executed here.

A real adapter would be tested separately using an explicitly selected source schema and authorized data. It should preserve source identifiers and versions, reject malformed rows and demonstrate how corrections are represented. Only then should its normalized output be passed to an allocation policy.

The allocator itself also has limits. It is an in-memory, single-call transformation. It does not implement concurrent updates, a persistent ledger, exchange rates, tax handling or arbitrary-precision amounts. The selected two-decimal domain is a fixture condition. Another domain needs another accepted rule.

These limitations keep the result useful rather than diminish it. Another engineer can inspect the arithmetic, reproduce the acceptance cases and decide which boundary their own system adds. They do not need to guess whether a synthetic example has quietly been presented as a live financial integration.

Download the complete attribution laboratory and raw samples. Run the check mode from the pinned project directory before the measure mode. The program uses only its literal synthetic input and produces a local JSON result.

A useful total should remain open to challenge

The practical standard is whether someone can trace a task-level amount back through its policy to the source records and unresolved terms. The report should survive a question about why a shared resource was assigned in a particular way without requiring the original author's memory.

That means preserving more than the final total. Keep the record identity, source scope, policy revision, remainder rule and any unallocated portion. Keep failed acceptance cases that explain why another input would be refused. Keep the raw execution observations separate from claims about the application's overall operation.

A cost attribution model is most useful when it helps a team identify which observation or decision is missing. It should not manufacture certainty by distributing every unresolved amount or by presenting a synthetic fixture as a measured service price.

The code in this article makes one small part of that discipline executable. It conserves the source, handles duplicates and conflicts, assigns residuals deterministically and exposes the boundary of its policy. Following a task through a cloud architecture then becomes a series of inspectable joins and decisions, rather than a single number whose meaning nobody can reconstruct.

Inspect the remainder rule before changing the implementation

The most compact part of the algorithm is also where an apparently harmless change can alter an output. For each source line, the routine converts the amount into its declared subunits and calculates the magnitude of each weighted share. It assigns the whole part first. The remainder is then distributed to the tasks with the largest fractional shares, with ordinal identifiers breaking equal remainders.

This is not equivalent to rounding every share independently. Independent rounding can make the allocated total differ from the original amount. It is also not equivalent to giving the leftover to the last item in the input. That would let a presentation or enumeration change alter the financial interpretation of the same weights.

The test for a one-subunit amount split three ways makes the policy visible. Under the declared tie-break, task-a receives the subunit and the other tasks receive zero. The result may look uneven, but it is deterministic and conserved. A different organization may prefer another residual policy. The correct engineering response is to version that policy and its expectations, not to conceal the choice.

The routine also applies the same sign to the distributed magnitudes of a negative line. That behavior is verified by a selected negative fixture. It should not be generalized to every external correction or adjustment type, because the adapter may need to distinguish multiple source meanings before the allocation stage.

For a code review, I would ask the implementer to show this small case before showing the timing result. If the team cannot agree on where one indivisible subunit belongs, a faster transformation will only make the disagreement harder to notice at scale. The acceptance fixture turns an implicit choice into a concrete decision that another person can inspect.

The project pins SDK 10.0.400 and runtime 10.0.15 with roll-forward disabled. The accepted JSON records remain in the package beside the source. A later run on another runtime should be identified as a separate observation; reproducing the arithmetic contract does not require reproducing the exact elapsed time of this development machine.

Source code and execution evidence

Download the complete lab, source record and raw execution results. This package supports the specific execution claims in this article. It is not a production certification.