Programming thread

  • 🐕 I am attempting to get the site runnning as fast as possible. If you are experiencing slow page load times, please report it.
Here's a little demonstration of a unit test:
Oh, I do that. That's all it is? I was confused because people talk about unit test frameworks and special libraries that exist solely to write unit tests. It doesn't make any sense to me because your example, as every such test I've ever written, is so specific to the code that it couldn't possibly need a library unless you're testing thousands of nearly identical units but you can't just use the same test over and over for some reason.
 
  • Like
Reactions: Marvin
Oh, I do that. That's all it is? I was confused because people talk about unit test frameworks and special libraries that exist solely to write unit tests. It doesn't make any sense to me because your example, as every such test I've ever written, is so specific to the code that it couldn't possibly need a library unless you're testing thousands of nearly identical units but you can't just use the same test over and over for some reason.
More or less.

In my experience, a lot of the value in testing frameworks lies in slotting your work into a bigger corporate environment.

So depending on the language, they'll include features for scanning your code and analyzing which logic branches actually got tested when your test ran. (ie if you have a branch like if (input.duration) { return input.duration * 60; } else { return 0; }and your test doesn't execute the second half, that code didn't get tested) This makes managers happy because they can read reports that say things like "81% of the lines of source code got touched by the tests". Then they can pester you whenever that number goes down.

Some actually useful features test frameworks offer are things like libraries to fake ("mock") onerous outside resources like databases and remote http servers. So if your code grabs data from some google web API, your test framework might offer a way to fake that just for the duration of the tests.

But yeah, just writing your own test code by hand will give you 99% of the value of testing.
 
Oh, I do that. That's all it is? I was confused because people talk about unit test frameworks and special libraries that exist solely to write unit tests. It doesn't make any sense to me because your example, as every such test I've ever written, is so specific to the code that it couldn't possibly need a library unless you're testing thousands of nearly identical units but you can't just use the same test over and over for some reason.
In more complicated object oriented code with shit like dependency injection etc, basically in any kind of corposhit code often in languages like Java or C#, you'll need a mocking framework to be able to plug in dummy objects required by your component, and some kind of unit test framework in order to have finer control over configuring how and when tests are run, setting up context for unit test suites, reporting test results, etc.

But in simple programs you don't necessarily need any of that stuff.
 
That's all it is?
Enterprise codeniggers try not to overcomplicate everything to truly ridiculous levels challenge at 3 AM (IMPOSSIBLE)
But in simple programs you don't necessarily need any of that stuff.
With a medium-sized chunk of test runner code, a few polymorphic objects in certain nondeterministic areas that that can be swapped out for dummy versions when tests are run, and a bit of careful design, you have a good way to test code without complicated frameworks.
 
So depending on the language, they'll include features for scanning your code and analyzing which logic branches actually got tested when your test ran.
Interestingly, there are a number of plugins for popular debuggers that can measure the code coverage of your tests and give you nice color coding on disassembly views. I have found them quite handy when testing C shit.
 
Enterprise codeniggers try not to overcomplicate everything to truly ridiculous levels challenge at 3 AM (IMPOSSIBLE)
Unit testing has nothing on the tomes of incomprehensible waffle written about dependency injection in this regard. Who knew you could make parameterization this complex?
 
Oh, I do that. That's all it is? I was confused because people talk about unit test frameworks and special libraries that exist solely to write unit tests. It doesn't make any sense to me because your example, as every such test I've ever written, is so specific to the code that it couldn't possibly need a library unless you're testing thousands of nearly identical units but you can't just use the same test over and over for some reason.
You are thinking of mocking frameworks. Those are used when you want to isolate the behavior of different sections of a system to detect failure.

For example, if you were connecting to a database, you would use the mocking framework to simulate correct output from the database. Then you wouldn't need to rely on the database while testing. The same thing goes for accessing a remote resource like a server somewhere else. You use the mocking framework to test your programs behavior to an expected response.
 
  • Agree
Reactions: 306h4Ge5eJUJ
Now I'm just going through and implementing the stages and the tests start passing, one by one.

1722140709188.png
success.png

And... we're passing all the tests.

Was kinda distracted by day job stuff during the week, but tonight (Saturday) since I got it passing the tests, I actually gave it a spin in a test environment with all the real resources it manages.

It works and I'm pleased how well it works, but the yaml output from this ocaml plugin I'm using is butt ugly. So I spent quite a bit of time polishing the yaml config format to make it somewhat readable without being painfully verbose.

All in all, happy with it. Now to work on actually implementing the actual functional parts that execute said config.



^ I wrote the above Saturday night but didn't hit Post Reply. But now it's Monday morning and I'm back at work.

I learned today that postgres's table and field names are case sensitive in a weird way. Basically they fold all your table and fieldnames in SQL queries to lowercase, but internally the names as stored are still case sensitive.

If you have a table named Account, then it'll bitch at you if you do this:

Code:
# select * from Account;
ERROR:  relation "account" does not exist
LINE 1: select * from Account;
See, even though you typed Account, in parsing the SQL, it folds it to lowercase. Except you are allowed to have case sensitive table names, so it craps out anyway.

So if you (or the hinky JS ORM your boss demands) use caps, you need to specifically quote table and field names. Your handwritten queries will look like update "Account" set "roleGroup" = "moderator" where "Account"."id"= 1234;.

Neat.
 
Anybody know where I can get a crash course on Ruby for free?

I don't give a flying fucking shit about your opinion on Ruby. One of my projects has some Ruby in it and any learning is a benefit because knowledge is power.

I need to learn Ruby to go the extra mile for one of my clients. I am trying to build my reputation, so I give a soaring fuck about happy clients. Call me the whore of coding because I don't wanna deliver code thats the best bang for their buck. I am trying to make code that is the best fuck for their buck to compensate for my lack of college.


Where are the free Ruby classes. FREE. A NIGGA IS BROKE AND HUNGRY.
 
Anybody know where I can get a crash course on Ruby for free?

I don't give a flying fucking shit about your opinion on Ruby. One of my projects has some Ruby in it and any learning is a benefit because knowledge is power.

I need to learn Ruby to go the extra mile for one of my clients. I am trying to build my reputation, so I give a soaring fuck about happy clients. Call me the whore of coding because I don't wanna deliver code thats the best bang for their buck. I am trying to make code that is the best fuck for their buck to compensate for my lack of college.


Where are the free Ruby classes. FREE. A NIGGA IS BROKE AND HUNGRY.
Geeksforgeeks.org. Shine on you beautiful retard.
 
Anybody know where I can get a crash course on Ruby for free?

I don't give a flying fucking shit about your opinion on Ruby. One of my projects has some Ruby in it and any learning is a benefit because knowledge is power.

I need to learn Ruby to go the extra mile for one of my clients. I am trying to build my reputation, so I give a soaring fuck about happy clients. Call me the whore of coding because I don't wanna deliver code thats the best bang for their buck. I am trying to make code that is the best fuck for their buck to compensate for my lack of college.


Where are the free Ruby classes. FREE. A NIGGA IS BROKE AND HUNGRY.
This should be good, maybe.
 
  • Like
Reactions: ${Sandy}
Anybody know where I can get a crash course on Ruby for free?

I don't give a flying fucking shit about your opinion on Ruby. One of my projects has some Ruby in it and any learning is a benefit because knowledge is power.

I need to learn Ruby to go the extra mile for one of my clients. I am trying to build my reputation, so I give a soaring fuck about happy clients. Call me the whore of coding because I don't wanna deliver code thats the best bang for their buck. I am trying to make code that is the best fuck for their buck to compensate for my lack of college.


Where are the free Ruby classes. FREE. A NIGGA IS BROKE AND HUNGRY.
find yourself a good torrent site (rutracker) or something like libgen (RIP, but they should still be around) and look for books. that's the best way to learn imo. and avoid packt,
 
All in all, happy with it. Now to work on actually implementing the actual functional parts that execute said config.
Continuing with this project, I've got a little bit of complexity coming up.

I've got to implement rate limiting a bitstream. I'm handling media streams that deliver packets containing media frames. I want to be able to limit the stream to a maximum bitrate. Very simple approach where, if we exceed the bitrate, just start dropping packets until we're under the rate again, and leave repairing the stream to the underlying codec/protocol.

In general, I prefer to lean on the capabilities of the language / library / compiler that I'm using. I think it's a bad idea to try and second guess (at first) the garbage collector or whatever tool you're using. Someone else has invested a lot of effort and engineering smarts into making it work well. It's good practice to lean into that.

Write simple, readable code first, and then if it's not performing to an acceptable level, then start digging in and seeing if you need to handle some special case manually.

In this case though... well, the naive logic of how to implement this kinda makes me worry a bit.

The naive logic would be "keep a linked list of every packet that passes through for the past X seconds, and every time a new packet comes in, add them all up, find the average bitrate, and drop/pass".

I don't know how I feel about possibly generating a continuous stream of garbage, in a tight main loop.

Plus the signature of how this module should work is super simple that, if I'm just sperging here for no justifiable engineering reason (distinct possibility), well, I can just tear it out and drop in a naive implementation.

Ok so, to the meat of the issue. I want to create a RateLimiter module with an Ocaml signature like this:
Code:
module RateLimiter :
  sig
    type t
    val create : max_bitrate:int -> unit -> t (* haven't added some settings, work in progress *)
    val record_and_check : t -> int -> bool
  end

Very simple. There's a RateLimiter.create ~max_bitrate () function that returns a RateLimiter.t, and then a RateLimiter.record_and_check rl byte_count function that records the incoming data and returns true or false depending on if the packet should be dropped.

There's a convention in Ocaml to create modules named after nouns, and they usually contain a main type just called t, and then a bunch of functions for manipulating the ts. You could call it type rate_limiter instead if you wanted. But t works.

So the algorithm I'm working on right now works by dividing our sample period (which could be a second, half a second, whichever, it'll be configurable in an argument to RateLimiter.create) into a number of "buckets". Each bucket records how many bits passed by in that chunk of the sample period.

So in a rough pseudocode, let's say we have:
Code:
buckets = [15, 10, 5, 10, 12, 8, 18, 4, 8, 2]
last_update_time = 100.33 # seconds
total_bitrate = 82
Let's say we are sampling for 1 second, and each of those buckets represents 0.1 second.

Based on a timestamp of 100.33, the current bucket is index 3, which currently has a value of 10 bits (I guess).

While our update time is below 100.4, we'll keep incrementing that 3-indexed bucket. So if current update time is 100.35 and the packet has 2 bits (very small packet!), our state should now be:
Code:
buckets = [15, 10, 5, 12, 12, 8, 18, 4, 8, 2]
last_update_time = 100.35 # seconds
total_bitrate = 84

But when we tick over into 100.4, we have to start recycling buckets. We subtract the value of the recycled bucket from the total_bitrate, zero out that value, and then add our new bits in. That's because that bucket is now longer, time-wise, than our sample period in the past, thus it no longer matters for the bitrate anymore.

So if we tick into 100.4 and we're handling a new packet with 7 bits, our state should look like this:
Code:
buckets = [15, 10, 5, 12, 7, 8, 18, 4, 8, 2]
last_update_time = 100.35 # seconds
total_bitrate = 79 # 84 - 12, then plus 7 bits

The one last unhandled case is if we go longer than the sample period for updates. The solution to that is easy; just zero everything out. Zero bits are zero bits.

I know I repeat myself a lot, but this is the sort of thing I really appreciate about Ocaml, is having the escape valve of side-effecting operations when you need them.

The majority of this program is very functional. High level concepts, mapping and iterating over functional structures. But in the very tightest loops, being able to write side effecting code when necessary is really convenient.

This would be super awkward in Haskell, I feel. (I suppose I shit on Haskell a lot comparing it to Ocaml and SML; one of these days I should do some project in Haskell so I know what I'm talking about. Probably not this one though.)

Oh, and one final thing, I'm using an Ocaml library called Mtime for the clock stuff. The main advantage over it instead of just using time(2) directly is that it isn't disturbed by system clock changes. Which, if you've got a daemon running for weeks on end, and some ntpd sync comes through, it won't get disturbed. Minor detail that has the potential to cause more profound problems.

Anyway, I have the logic written, just writing the tests. The meat of the above algorithm is contained in this code:
Code:
let update_buckets t =
  let new_span = Mtime_clock.count t.start in
  if new_span >= t.cycle_duration
  then reset_to_zero t
  else
    let last_index = span_index t t.last_span in
    let new_index  = span_index t new_span in

    let rec aux ~index =
      let index = index % Array.length t.buckets in
      t.total <- t.total - t.buckets.(index);
      t.buckets.(index) <- 0;
      if index >= new_index
      then ()
      else aux ~index:(index + 1)
    in

    aux ~index:(last_index + 1);
    t.last_span <- new_span
The high level RateLimiter.record_and_check function internally calls update_buckets, then it just finishes up the bookkeeping and returns true or false.
 
  • Informative
Reactions: Jarry Seinfelt
I find these OCaml posts really interesting. I've written some code in SML and Haskell, but most of that was in college. I'd say I've written precisely enough Haskell to get why it's not a great fit for production stuff - I have run into enough situations where I could implement my logic trivially if I had, for example, an actual looping construct. But getting it into Haskell is "square peg in a round hole" type deal. In those instances, your gratitude to the type checker for mostly eliminating your need for tests basically disappears. When you know what you're doing you can write really elegant code that expresses your library or application's logic on a first glance, but if not, it seems like you're up shit creek without a paddle.

I don't really get OCaml's module system but I haven't really written anything in it, or read any resources on it, so that doesn't exactly bother me. I'd learn it when I have to / have interest to.

Right now I'm just working on relearing C in my spare time. It's pretty unlikely that I'll write any C for work any time soon, but after working in dynamic, GC'd land for so long it's actually pretty interesting and I enjoy it. Makes me think about all the conveniences that Python, etc. give you; how they're implemented, how they could go wrong, stuff like that. Plus I feel like being able to talk somewhat intelligently about memory management is a decent signal that you take your shit seriously and aren't some bootcamp kiddie, at least in my field.
 
The naive logic would be "keep a linked list of every packet that passes through for the past X seconds, and every time a new packet comes in, add them all up, find the average bitrate, and drop/pass".
I made a rate limiter last Friday with this:
(disclaimer: dev.to sucks troon dick)

Generic Cell Rate Algorithm​

Generic Cell Rate Algorithm (GCRA) comes from a communications technology called Asynchronous Transfer Mode (ATM) and was used in ATM network’s schedulers to delay or drop cells, small fixed size packets of data, that came in over their rate limit.
O(users) memory, simple addition, can adjust the rate midstream.
(My case was user-generated photos per corporate user, nothing so frequent as frames.)
 
I made a rate limiter last Friday with this:
(disclaimer: dev.to sucks troon dick)


O(users) memory, simple addition, can adjust the rate midstream.
(My case was user-generated photos per corporate user, nothing so frequent as frames.)
So this algorithm is simpler than mine. One difference is that it deals with equally sized requests whereas I'm dealing with variably sized requests, but that shouldn't be too difficult to deal with. Basically just treating an N bit packet as N requests in a burst.

I'm trying to figure out if it does a rolling window somehow without the multiple cell approach I went with, or if it just has a fixed window (which is fine for lots of uses, and honestly is probably fine for my situation as well).

Idk I'm back at work now, so back to pajeetery. I'll need to sit down later and really digest this.

Also sorta unrelated, but I was messing with redis the other day for some work stuff, and I thought I remembered there was some big CoC drama involving redis. But now I can't find it and I'm wondering if I imagined it or confused it with some other big open source CoC drama (OpalGate comes to mind, I might've been confusing it with the OpalGate stuff). Anyone remember?
 
Also sorta unrelated, but I was messing with redis the other day for some work stuff, and I thought I remembered there was some big CoC drama involving redis. But now I can't find it and I'm wondering if I imagined it or confused it with some other big open source CoC drama (OpalGate comes to mind, I might've been confusing it with the OpalGate stuff). Anyone remember?
You might be mixing it up with Redis' recent licence drama - it switched from something Stallman-friendly to roughly the same restricted category as Mongo.
 
So this algorithm is simpler than mine. One difference is that it deals with equally sized requests whereas I'm dealing with variably sized requests, but that shouldn't be too difficult to deal with. Basically just treating an N bit packet as N requests in a burst.
For variably-sized requests (I also have variably-sized requests), just after he calculates "separation", immediately scale it by request size and use it further on. Wikipedia says scaling can be too computationally intensive for some purposes (not for mine though) but you might be able to ignore length or use bit operations to scale it approximately.

For anyone watching at home, his python+redis implementation isn't perfect (for a python + redis implementation):
  • python's redis library is somewhat different now, use client.set with kwargs;
  • redis keys should have an expiration time set
  • I also got rid of "round" and used floats and time with milliseconds because I'm doing data soyence, we can afford to multiply a float every once in a while, and so can everyone working with python
I'm trying to figure out if it does a rolling window somehow without the multiple cell approach I went with, or if it just has a fixed window (which is fine for lots of uses, and honestly is probably fine for my situation as well).
It simulates a rolling window.
 
Back