<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Softalink LLC</title>
    <description>The latest articles on DEV Community by Softalink LLC (@softalink).</description>
    <link>https://dev.to/softalink</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4019933%2Fa324a752-0118-4b01-9eca-e3a17ead6fd9.png</url>
      <title>DEV Community: Softalink LLC</title>
      <link>https://dev.to/softalink</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/softalink"/>
    <language>en</language>
    <item>
      <title>We Rewrote VictoriaMetrics in Rust. It Now Beats the Original on Every Benchmark Metric</title>
      <dc:creator>Softalink LLC</dc:creator>
      <pubDate>Tue, 07 Jul 2026 17:02:38 +0000</pubDate>
      <link>https://dev.to/softalink/we-rewrote-victoriametrics-in-rust-it-now-beats-the-original-on-every-benchmark-metric-2g62</link>
      <guid>https://dev.to/softalink/we-rewrote-victoriametrics-in-rust-it-now-beats-the-original-on-every-benchmark-metric-2g62</guid>
      <description>&lt;p&gt;VictoriaMetrics is one of the fastest open-source time-series databases in existence. Its Go implementation is not naive code that a rewrite trivially beats — it is a decade of careful engineering &lt;em&gt;around&lt;/em&gt; the garbage collector: object pools everywhere, unsafe byte↔string casts, buffer recycling as a way of life.&lt;/p&gt;

&lt;p&gt;That made it the perfect subject for a question we wanted a rigorous answer to: &lt;strong&gt;how much performance is left on the table when the same algorithms run without a GC?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Today we're releasing the answer as &lt;a href="https://github.com/softalink/esmetrics" rel="noopener noreferrer"&gt;EsMetrics&lt;/a&gt;, an Apache-2.0 licensed, from-scratch Rust implementation of VictoriaMetrics single-node (reference v1.146.0). On the &lt;a href="https://github.com/timescale/TSBS" rel="noopener noreferrer"&gt;TSBS&lt;/a&gt; benchmark suite it outperforms the original Go server on &lt;strong&gt;every metric&lt;/strong&gt; — ingestion throughput and query latency across all ten devops query types — on both Linux and Windows, while answering queries &lt;strong&gt;byte-for-byte identically&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;p&gt;TSBS &lt;code&gt;cpu-only&lt;/code&gt;, scale 100 (8.64 million metrics), four workers, medians of three paired back-to-back rounds against the official v1.146.0 release binaries on identical hardware:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ingestion&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;platform&lt;/th&gt;
&lt;th&gt;Go v1.146.0&lt;/th&gt;
&lt;th&gt;EsMetrics&lt;/th&gt;
&lt;th&gt;delta&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Linux (4 cores)&lt;/td&gt;
&lt;td&gt;3.59M metrics/s&lt;/td&gt;
&lt;td&gt;5.14M metrics/s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+43%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows 11 (8 cores)&lt;/td&gt;
&lt;td&gt;2.99M metrics/s&lt;/td&gt;
&lt;td&gt;4.95M metrics/s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+66%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Queries&lt;/strong&gt; — all ten TSBS query types are faster. Mean latency improvements range from −11% to −59% on Linux and −52% to −82% on Windows. The heaviest query in the suite (&lt;code&gt;double-groupby-all&lt;/code&gt;, aggregating a thousand series) went from 102ms to 41ms on Windows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Correctness&lt;/strong&gt; — 750 replayed TSBS queries produce byte-identical JSON to the Go server, down to Go's shortest-round-trip float formatting. Roughly 600 tests translated from the upstream Go suites run in CI on Linux and native Windows.&lt;/p&gt;

&lt;p&gt;All raw per-round data, the harnesses, and the honest caveats live &lt;a href="https://github.com/softalink/esmetrics/tree/main/benchmarks/results" rel="noopener noreferrer"&gt;in the repo&lt;/a&gt;. The short version of the caveats: two machines, one workload family, and a handful of sub-millisecond stats that flip within the TSBS client clock's ~0.5ms Windows quantization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Byte-identical or it didn't happen
&lt;/h2&gt;

&lt;p&gt;The single most valuable decision in the project was a correctness rule: &lt;strong&gt;every response must match the Go server byte-for-byte.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That sounds obsessive. It is. It's also the only reason we can publish benchmark numbers with a straight face — a "faster" database that returns subtly different aggregates is just a broken database with good marketing.&lt;/p&gt;

&lt;p&gt;The rule had teeth. It forced us to port Go's exact float formatting (Go's &lt;code&gt;strconv&lt;/code&gt; produces shortest-round-trip decimal in &lt;em&gt;fixed&lt;/em&gt; notation — &lt;code&gt;1e21&lt;/code&gt; prints as &lt;code&gt;1000000000000000000000&lt;/code&gt;), the exact staleness-interval semantics of the rollup engine, the exact tie-breaking in the deduplication algorithm, and the exact JSON escaping of the response templates. A replay harness diffed our responses against the Go binary on identical data after every optimization. It caught more real bugs than the ported unit tests did.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the performance actually came from
&lt;/h2&gt;

&lt;p&gt;The straight algorithmic port — faithful data structures, idiomatic Rust — won the ingestion benchmark immediately and &lt;em&gt;lost most of the query benchmarks&lt;/em&gt;. Everything after that was profile-driven. A few of the changes that mattered:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The parallel unpacking pipeline.&lt;/strong&gt; Go's &lt;code&gt;netstorage&lt;/code&gt; collects per-series block references without decoding, then unpacks blocks across per-CPU workers. Our first port did all of that on the HTTP connection thread; the profile showed 26% of query time in series materialization and 30% in zstd/varint decode, all serialized. Porting the two-pass design — reference collection, then decode+merge across a persistent work-stealing pool with per-worker scratch buffers — cut the heaviest query's latency by 60%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Caching what the upstream re-decodes.&lt;/strong&gt; VictoriaMetrics decompresses data blocks on every query. We added a size-bounded, sharded decoded-block cache keyed by (part, offset), invalidated on part drop. This is a deliberate deviation from the faithful port — and it's worth 25-60% on read-heavy queries. Deterministic destruction made the invalidation story clean: when the last reference to a merged-away part drops, its cache entries go with it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Allocation discipline where it counts.&lt;/strong&gt; The ingest path is allocation-free at steady state: a zero-copy Influx parser that borrows from the request buffer (lifetimes prove what Go's &lt;code&gt;unsafe&lt;/code&gt; casts merely hope), thread-local conversion arenas, and an ingestion API that never clones metric names. Sharded caches keyed by xxh64 with precomputed hashes — one hash per lookup — replaced the standard library's SipHash maps on the hot path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Small mechanical wins that compound.&lt;/strong&gt; SWAR varint decoding (eight bytes at a time instead of restart-per-byte). K-way merging of overlapping blocks instead of concatenate-and-stable-sort. A fair round-robin evaluation pool so a heavy query can't starve the sub-millisecond ones behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Windows cliff
&lt;/h2&gt;

&lt;p&gt;The most instructive chapter was Windows. Under Wine, everything looked great. On &lt;em&gt;real&lt;/em&gt; Windows hardware, multi-series queries were suddenly &lt;strong&gt;seven times slower&lt;/strong&gt; than Go — 748ms for a query Go served in 102ms — while ingestion still won comfortably.&lt;/p&gt;

&lt;p&gt;The profile pattern was strange: the degradation scaled linearly with series count, roughly half a millisecond per series. That's not an algorithm problem; that's a &lt;em&gt;per-allocation&lt;/em&gt; problem. The Windows default process heap serializes concurrent allocations under a global lock. Go never notices — it ships its own allocator. Rust, by default, uses the system allocator.&lt;/p&gt;

&lt;p&gt;One line — &lt;code&gt;#[global_allocator] static GLOBAL: MiMalloc&lt;/code&gt; — took that query from 748ms to 81ms.&lt;/p&gt;

&lt;p&gt;Windows had two more lessons that required structural fixes rather than tweaks. WinSock's &lt;code&gt;shutdown()&lt;/code&gt; doesn't interrupt a thread blocked in &lt;code&gt;recv&lt;/code&gt; the way POSIX does, so graceful shutdown needed ticked reads on idle connections. And deleting a merged-away data part's directory costs tens of milliseconds on Windows (hello, Defender) — which occasionally landed on whichever &lt;em&gt;query thread&lt;/em&gt; happened to drop the last reference. The fix mirrors what the upstream does: a dedicated background remover thread, with drains at close boundaries so tests and restarts stay correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the borrow checker found
&lt;/h2&gt;

&lt;p&gt;Porting is a brutal code review. Two findings stand out.&lt;/p&gt;

&lt;p&gt;The concurrency one: under concurrent ingestion, series registration had a window where two workers could assign different TSIDs to the same new series — in one benchmark load we counted 2,214 "shadow" series, which silently inflated every query that touched the affected hour. Striped single-flight locks closed it. The race pattern exists upstream in a milder form; the Rust port made it visible because our profiling kept asking why Windows queries unpacked 3.2× more series than expected.&lt;/p&gt;

&lt;p&gt;The subtle one: our port of Go's &lt;code&gt;regexp/syntax&lt;/code&gt; used &lt;code&gt;char::to_lowercase&lt;/code&gt;/&lt;code&gt;to_uppercase&lt;/code&gt; to approximate Go's &lt;code&gt;unicode.SimpleFold&lt;/code&gt;. But simple-fold orbits must be &lt;em&gt;closed cycles&lt;/em&gt;, and the std mappings aren't — U+212A (KELVIN SIGN) folds to &lt;code&gt;k&lt;/code&gt;, but nothing folds back to it. Any &lt;code&gt;while f != c&lt;/code&gt; orbit walk over a negated character class became an infinite loop. If you're implementing case-insensitive matching by hand: bound your orbit walks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scope, honestly
&lt;/h2&gt;

&lt;p&gt;EsMetrics implements what the TSBS benchmark and standard Prometheus-style usage exercises: Influx line-protocol ingestion, the Prometheus query API, the full storage engine, retention, deduplication. It does not (yet) do clustering, the agent/alerting toolchain, other ingestion protocols, or the web UI. Versioning mirrors the upstream: EsMetrics 1.146.x tracks VictoriaMetrics v1.146.0, and a scripted sync process (baselines pinned, upstream diffs auto-mapped to the Rust modules that port them) is how it stays current.&lt;/p&gt;

&lt;h2&gt;
  
  
  Credits and an experiment note
&lt;/h2&gt;

&lt;p&gt;EsMetrics is developed by Softalink LLC, with Claude (Anthropic) as an engineering contributor — the porting blueprints, the profile-driven optimization iterations, and the benchmark harness work were done in close human-AI collaboration. We think the result — a benchmark-complete, byte-identical port of a seriously optimized production database — is an interesting data point on what that collaboration can produce, and all of the evidence is public for scrutiny.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code: &lt;a href="https://github.com/softalink/esmetrics" rel="noopener noreferrer"&gt;https://github.com/softalink/esmetrics&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Release (Linux + Windows binaries): &lt;a href="https://github.com/softalink/esmetrics/releases/tag/v1.146.0" rel="noopener noreferrer"&gt;https://github.com/softalink/esmetrics/releases/tag/v1.146.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Benchmarks and methodology: &lt;a href="https://github.com/softalink/esmetrics/tree/main/benchmarks/results" rel="noopener noreferrer"&gt;https://github.com/softalink/esmetrics/tree/main/benchmarks/results&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Contact: &lt;a href="mailto:info@softalink.com"&gt;info@softalink.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;EsMetrics is Apache-2.0, a derivative work of VictoriaMetrics (Copyright VictoriaMetrics, Inc.). If your organization wants commercial support, sponsored features, or benchmark validation on your hardware — we'd love to hear from you.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>timeseries</category>
      <category>database</category>
      <category>iot</category>
      <category>rust</category>
    </item>
    <item>
      <title>Bending draw.io into 2.5D: how I built isometric shearing as a plugin</title>
      <dc:creator>Softalink LLC</dc:creator>
      <pubDate>Tue, 07 Jul 2026 16:58:16 +0000</pubDate>
      <link>https://dev.to/softalink/bending-drawio-into-25d-how-i-built-isometric-shearing-as-a-plugin-o4n</link>
      <guid>https://dev.to/softalink/bending-drawio-into-25d-how-i-built-isometric-shearing-as-a-plugin-o4n</guid>
      <description>&lt;h2&gt;
  
  
  The itch
&lt;/h2&gt;

&lt;p&gt;Isometric diagrams make infrastructure, network, and architecture drawings instantly readable. The problem is the tooling: you either pay for a dedicated isometric app, or you hand-fake the perspective in a general tool and fight it forever. Meanwhile the general tool most of us already have open is &lt;strong&gt;draw.io&lt;/strong&gt; — free, open source, offline-capable, and embedded in Confluence, GitLab, and VS Code.&lt;/p&gt;

&lt;p&gt;So instead of leaving draw.io, I taught it to go isometric. The result is &lt;strong&gt;EsDraw&lt;/strong&gt;: right-click any shape, pick a direction, and it snaps onto a 2.5D isometric plane. There's a &lt;a href="https://softalink.github.io/esdraw/" rel="noopener noreferrer"&gt;live demo&lt;/a&gt; — a real self-hosted draw.io with the plugin already loaded and a sample scene open — and the code is &lt;a href="https://github.com/softalink/esdraw" rel="noopener noreferrer"&gt;MIT on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't eyeball isometric — match a real one
&lt;/h2&gt;

&lt;p&gt;The fastest way to make isometric art look "off" is to guess the angle. I didn't want guesses, so I reverse-engineered a tool people already trust for this: &lt;strong&gt;icograms Designer&lt;/strong&gt;. Poking at its live DOM and SVG transforms, the model is clean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;2:1 isometric&lt;/strong&gt; projection at exactly &lt;strong&gt;26.565°&lt;/strong&gt; — that's &lt;code&gt;atan(0.5)&lt;/code&gt;, the angle whose tangent is ½.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Four absolute states&lt;/strong&gt;, not additive nudges: shear Left, Right, Clockwise, Counterclockwise. "Left then Right" lands on Right — each is a fixed transform about the shape's centre, applied fresh.&lt;/li&gt;
&lt;li&gt;On selection, icograms draws &lt;strong&gt;crosshair "align lines"&lt;/strong&gt; — a vertical axis plus two diagonals — so you can see the isometric planes. EsDraw reproduces those too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reducing the effect to four matrices (and their inverses) meant the whole feature could live as pure, testable math instead of a pile of special cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  mxGraph doesn't want you to do this
&lt;/h2&gt;

&lt;p&gt;draw.io is built on the &lt;strong&gt;mxGraph&lt;/strong&gt; library, which renders cells to SVG but exposes no "apply an arbitrary transform to this cell and everything it owns" hook. So EsDraw monkeypatches a small set of prototypes — &lt;code&gt;mxShape&lt;/code&gt;, &lt;code&gt;mxText&lt;/code&gt;, &lt;code&gt;mxCellRenderer&lt;/code&gt;, &lt;code&gt;mxVertexHandler&lt;/code&gt; — to inject the shear at render time.&lt;/p&gt;

&lt;p&gt;Shearing a plain box is the easy 10%. The other 90% is everything &lt;em&gt;attached&lt;/em&gt; to the box:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Labels.&lt;/strong&gt; &lt;code&gt;mxText&lt;/code&gt; overrides &lt;code&gt;redraw&lt;/code&gt; separately, so it needs its own patch — and the text has to shear about the &lt;em&gt;cell's&lt;/em&gt; centre, not the label's bounding box, or it drifts off the shape.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container and list children.&lt;/strong&gt; In draw.io a container's contents are independent cells. Shearing the parent does nothing to them unless the shear &lt;em&gt;resolves up the ancestor chain&lt;/em&gt; to the nearest sheared parent and transforms each child about that parent's centre. Same story for list items, table rows, and cells.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fold controls.&lt;/strong&gt; The collapse/expand chevron is drawn by a different code path (&lt;code&gt;redrawControl&lt;/code&gt;) and has to ride along on the sheared header.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connectors.&lt;/strong&gt; You can't hand an edge a single transform when its two endpoints belong to shapes sheared about &lt;em&gt;different&lt;/em&gt; centres. Instead every point on the edge follows its own terminal's shear, so arrowheads stay glued to the (now isometric) connection points.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The deep end: the selection box and resize
&lt;/h2&gt;

&lt;p&gt;The thing that took the longest was the least glamorous: the &lt;strong&gt;dashed selection rectangle and its resize handles&lt;/strong&gt;. Left alone, they stubbornly stay axis-aligned around a shape that clearly isn't — it looks broken. mxGraph redraws those handles through &lt;code&gt;redrawHandles&lt;/code&gt; (not the shape's &lt;code&gt;redraw&lt;/code&gt;), so the shear had to be applied there too, to the border, the sizers, and the rotation handle.&lt;/p&gt;

&lt;p&gt;Then resizing had to actually &lt;em&gt;work&lt;/em&gt; along the sheared axes. When you drag a handle, mxGraph hands you a delta in screen space; EsDraw &lt;strong&gt;un-shears that delta back onto the cell's own axes&lt;/strong&gt;, then adds a &lt;strong&gt;centre-shift compensation&lt;/strong&gt; so the shape resizes in place instead of sliding away as its centre moves. Getting that right is what makes dragging feel like you're resizing an isometric object rather than a flat one wearing a costume.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture: pure core, thin patches
&lt;/h2&gt;

&lt;p&gt;The guiding rule was simple: &lt;strong&gt;if it's math, it's a pure tested function; if it touches the DOM, it's a thin, replaceable patch.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;core&lt;/strong&gt; (&lt;code&gt;src/iso/*&lt;/code&gt;) — style parsing, the four matrices, point/delta transforms, edge-endpoint math, resize math — has &lt;strong&gt;no mxGraph imports&lt;/strong&gt; and sits at &lt;strong&gt;100% unit coverage&lt;/strong&gt; via Node's built-in test runner.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;glue&lt;/strong&gt; monkeypatches mxGraph and mostly just delegates to the core.&lt;/li&gt;
&lt;li&gt;Browser behaviour is checked end-to-end with &lt;strong&gt;Playwright&lt;/strong&gt; in a real editor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That split is why the tricky geometry is verifiable in isolation and the integration is provable in a browser — and why every reverse-engineered finding (angles, transforms, align lines) could be written up in &lt;code&gt;docs/design&lt;/code&gt; as the actual spec the code matches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it, break it
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://softalink.github.io/esdraw/" rel="noopener noreferrer"&gt;live demo&lt;/a&gt; is itself a small engineering artifact: CI fetches a pinned draw.io build, injects the plugin, and deploys it to GitHub Pages with a sample isometric scene already open — so you're clicking real draw.io, not a mock.&lt;/p&gt;

&lt;p&gt;EsDraw is alpha (v0.1) and MIT-licensed. If you make diagrams for a living, I'd love to know where it breaks — exotic shapes, weird nesting, resize feel. Issues and stars welcome: &lt;strong&gt;&lt;a href="https://github.com/softalink/esdraw" rel="noopener noreferrer"&gt;https://github.com/softalink/esdraw&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>drawio</category>
      <category>lowcode</category>
      <category>isometric</category>
      <category>diagramming</category>
    </item>
  </channel>
</rss>
