mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-08-07 01:46:53 +01:00
Drop the two new KB articles from this PR
The Execution-discipline change + suggested-code propagation in the skills is the structural fix. The two knowledge articles (case-must-handle-unknown-enum-values, instream-length-unreliable-for-bc-streams) were T4 follow-ups derived from a single parity case study; they need broader review before landing as canonical BCQuality knowledge and are out of scope for this PR. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
31b9949235
commit
539be9d735
6 changed files with 0 additions and 202 deletions
|
|
@ -1,29 +0,0 @@
|
|||
codeunit 50272 "Sample InStream Length Bad"
|
||||
{
|
||||
var
|
||||
MaxSimpleUploadSize: Integer;
|
||||
|
||||
procedure Upload(var Stream: InStream; FileName: Text)
|
||||
var
|
||||
SimpleResp: HttpResponseMessage;
|
||||
ChunkedResp: HttpResponseMessage;
|
||||
begin
|
||||
MaxSimpleUploadSize := 4 * 1024 * 1024;
|
||||
// Wrong: Stream.Length is 0 or unreliable for streams from HTTP
|
||||
// responses, some file APIs, and caller-supplied streams.
|
||||
if Stream.Length <= MaxSimpleUploadSize then
|
||||
UploadSimple(Stream, FileName, SimpleResp)
|
||||
else
|
||||
UploadChunked(Stream, FileName, ChunkedResp);
|
||||
end;
|
||||
|
||||
local procedure UploadSimple(var Stream: InStream; FileName: Text; var Response: HttpResponseMessage)
|
||||
begin
|
||||
// ... PUT to /items/{id}/content endpoint
|
||||
end;
|
||||
|
||||
local procedure UploadChunked(var Stream: InStream; FileName: Text; var Response: HttpResponseMessage)
|
||||
begin
|
||||
// ... POST to /items/{id}/createUploadSession endpoint, then PUT in chunks
|
||||
end;
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
codeunit 50273 "Sample InStream Length Good"
|
||||
{
|
||||
var
|
||||
MaxSimpleUploadSize: Integer;
|
||||
|
||||
procedure Upload(var Stream: InStream; FileName: Text)
|
||||
var
|
||||
TempBlob: Codeunit "Temp Blob";
|
||||
SizedStream: InStream;
|
||||
BufferLength: Integer;
|
||||
SimpleResp: HttpResponseMessage;
|
||||
ChunkedResp: HttpResponseMessage;
|
||||
begin
|
||||
MaxSimpleUploadSize := 4 * 1024 * 1024;
|
||||
// Materialise once into a Temp Blob; its length is reliable.
|
||||
CopyStream(TempBlob.CreateOutStream(), Stream);
|
||||
BufferLength := TempBlob.Length();
|
||||
TempBlob.CreateInStream(SizedStream);
|
||||
|
||||
if (BufferLength > 0) and (BufferLength <= MaxSimpleUploadSize) then
|
||||
UploadSimple(SizedStream, FileName, SimpleResp)
|
||||
else
|
||||
UploadChunked(SizedStream, FileName, ChunkedResp);
|
||||
end;
|
||||
|
||||
local procedure UploadSimple(var Stream: InStream; FileName: Text; var Response: HttpResponseMessage)
|
||||
begin
|
||||
// ... PUT to /items/{id}/content endpoint
|
||||
end;
|
||||
|
||||
local procedure UploadChunked(var Stream: InStream; FileName: Text; var Response: HttpResponseMessage)
|
||||
begin
|
||||
// ... POST to /items/{id}/createUploadSession endpoint, then PUT in chunks
|
||||
end;
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: performance
|
||||
keywords: [instream, outstream, length, stream, upload, blob, http, chunk]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# `InStream.Length` is unreliable for branching on payload size
|
||||
|
||||
## Description
|
||||
|
||||
`InStream` exposes a `Length` property that returns the total byte count of the underlying buffer when the runtime can determine it. The catch is that "when the runtime can determine it" depends on **how the stream was obtained**:
|
||||
|
||||
- Streams produced from a `Blob` or a `Temp Blob` field by `CreateInStream` — `Length` is reliable; the blob is fully materialised.
|
||||
- Streams produced from a Media or MediaSet field — same: backed by a known-size payload.
|
||||
- Streams produced from `HttpResponseMessage.Content.ReadAs` and from many `File.*` APIs — `Length` may return `0` or a partial value, because the underlying transport is consumed incrementally and the total length is not known until the stream is exhausted.
|
||||
- Streams from `Stream` parameters supplied by callers — depends entirely on what the caller passed in.
|
||||
|
||||
Branching upload behaviour on `Stream.Length` is the common failure mode. The pattern is:
|
||||
|
||||
```al
|
||||
if Stream.Length <= MaxSimpleUploadSize then
|
||||
UploadSimple(Stream)
|
||||
else
|
||||
UploadChunked(Stream);
|
||||
```
|
||||
|
||||
For a Microsoft Graph drive upload, `MaxSimpleUploadSize` is 4 MB. If `Stream.Length` returns `0` (because the stream came from an HTTP response or a freshly written outstream that the runtime cannot size cheaply), the code takes the simple-upload path with a 10 MB file behind it, the API returns `413 Payload Too Large`, and the upload fails. The error surfaces to the user as a generic HTTP failure with no obvious connection to the buggy size check.
|
||||
|
||||
The same trap applies to any code that "skips the work if the stream is empty": `if Stream.Length = 0 then exit;` silently drops payloads when the stream came from a source that does not pre-compute length.
|
||||
|
||||
## Best Practice
|
||||
|
||||
Decide which behaviour you actually need.
|
||||
|
||||
- **Always-chunked** is the safe default when the stream's origin is not under your control. Chunked uploads work for any payload size; the per-chunk overhead is small for small payloads.
|
||||
- When a size threshold is genuinely required (for example, choosing between two endpoints with different cost profiles), copy the stream into a known-size buffer first — typically a `Temp Blob` — and read length from the blob, which IS reliable. The cost of one round-trip through a blob is acceptable for the upload-routing decision.
|
||||
- When the threshold is informational (logging, telemetry), guard against `0`: `if (Stream.Length > 0) and (Stream.Length <= Threshold)` so an unknown size routes to the safe path, not the optimistic one.
|
||||
|
||||
See sample: `instream-length-unreliable-for-bc-streams.good.al`.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Branching upload size, validation, or buffer allocation directly on `Stream.Length` when the stream's origin is anything other than a freshly-materialised blob. Detection signal: `Stream.Length` (or `.Length` on a variable typed `InStream` or `OutStream`) appearing as the left or right side of `<=`, `<`, `>=`, `>`, or `=` against a size-like constant or `Label`, with no prior copy through a `Blob`. The narrower signal — branching simple-vs-chunked upload on `Length` for Graph or REST endpoints with a documented size cap — is the high-confidence case.
|
||||
|
||||
See sample: `instream-length-unreliable-for-bc-streams.bad.al`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue