Skip to content

Announcing node-perfectapi version 1.0.0

I am happy to announce the release of node-perfectapi version 1.0.   This release improves stability, and improves scalability.  For a brief intro to perfectapi, just follow the link.

I’ll talk about scalability a little bit because it is an important new feature.  As you probably know, Node.js applications  run by default in a single process.   It is easy to use code to workaround this limitation (at least within a single machine), but it is up to the programmer to know how best to do that in a way that makes their code scale appropriately.

What I did for this release is simplify that decision a little bit for the developer, by automatically scaling the perfectapi code across multiple CPUs.   This means that HTTP requests and responses are handled by separate processes than the developer’s code.  Any errors in that code will be isolated, and any delays in that code will not affect the developers code.

For the developers, their own code can still make use of additional workers for CPU intensive tasks.  There is an example in the source code that demonstrates how to do this, as well as  some benchmarks that show expected gains when using additional workers.

 

Benchmarking APIs using PerfectAPI vs Express.js vs Restify.js

[Update: since these benchmarks were run, performance of Restify has improved substantially to where its performance is on a par with the others.  I have not got around to updating the charts in this article to reflect that].

In the cloud today, performance is more important than ever because it can be so easily correlated with cost.  If my app can handle 100 requests per second using a single CPU in the cloud, then that means that it will cost me 10 cpus if I need to handle 1,000 requests per second.  In the cloud, 10 CPUs cost 10x as much as 1 cpu.

PerfectAPI is my open-sourced Node.js-based service API module.  It is designed to be used as an API server that’s easy to write and easy to use.  It is mildly RPC-biased, although it includes many REST principles and exposes similar HTTP endpoints.  I wanted to run some benchmarks in order to validate its performance.

I chose to compare to Express.js because PerfectAPI uses Express.js and so it could not possibly perform any better.  (So its a good baseline).   I chose to also compare to Restify.js because it is a similar product.

Benchmarks were run on a small Amazon EC2 instance using ApacheBench running on a separate EC2 instance.  I used TJ Holowaychuk’s guidance to plot the charts.  Ok, on with the results…

First off, Express.js is a performance god.  Although I wasn’t able to replicate these results, my tests regularly averaged a very respectable 1,600-1,700 requests per second.   My sample app for PerfectAPI did around 1,000 requests per second.  Restify.js, I’m sorry to say, was much slower – around 250 requests per second.   Memory usage for all 3 was very low, as expected with Node.  The text results are all here, with the associated code here.

Here is a chart showing all three (note, this chart is out of date since Restify performance issues were resolved):

As you can see, Restify.js is not in the same league.  I’m sure it is nothing structural, it probably just needs some performance tuning.  Excluding Restify, the same chart looks like this:

You can see that as expected, Express.js is faster.  PerfectAPI is no slouch though, and scales similarly.  I don’t think the upturns at the end mean anything significant – to prove that to myself, I doubled the run to 16,000 and you can see that the performance is consistent over the length (although it has another upturn at the end).

Well that’s all I have really.  Please comment below if you have questions or observations.

Opinionated (RPC) APIs vs RESTful APIs

If you are not already aware, a few years ago there was some debate on the Internet as to when it was ok to call an API “RESTful”.  I won’t go into all the details, but suffice it to say that the well-respected originator of the term REST did not agree with the way his ideas were being implemented.  One outcome of this debate was that we (the loose community of API developers) now use the ugly acronym HATEOAS to refer to the style of REST which its creator envisioned, and the purists don’t freak out too much when people call their “other” APIs RESTful.

As of 2012, the scale of HTTP web services looks something like this (overlapping circles indicate shared principles):

Scale of web service architectural styles

My own API product (node-perfectapi) is biased towards the RPC side, with many of the advantageous RESTful principles built-in.  It is that way because I am a proponent of opinionated APIs, and I don’t think that the document/resource architectural style is a good fit for that.  In fact, I don’t think it is a good fit for any scenario where your data is not naturally a document.

I had a sneaking suspicion I might be missing some greater truth though – after all, the constraints on the right hand side must have associated benefits, right?  So I started investigating…

The Benefits of REST

One of the perceived benefits of doing REST is that the many intermediate layers in the Internet can handle caching.  I looked at the popular caching product Varnish, and was surprised that it is unable to automatically take advantage of even the most basic REST principles.  Out of the box, it caches the GETs that you explicitly tell it to.  Cache invalidations are configured manually.  In my research, other caching products seem similar.   The bottom line is that as long as you use idempotent GETs for querying data, you are doing just fine.  Beyond that, there is no inherent caching advantage to be gained by doing REST.

Some other benefits are more of a slam-dunk.  Content negotiation (return JSON or XML based on the request headers)  is a nice way to ensure that clients can talk in the language that is most natural for them.  Stateless servers are a well-proven boon to scalability.  But… these same principles are easy to incorporate into RPC too.

The one RESTful thing that is unnatural for RPC is the document/resource oriented paradigm of “one URI for each resource”.  There is a benefit of consistency, in that the same resource format to POST/PUT a resource is what you GET back when query the resource.  It promotes a nice warm feeling in my tummy when things are so nice and symmetrical.  Documentation is simpler because you spend time documenting the format of the resource once, instead of documenting several RPC functions, each of which may involve some or all of the same resource.  Community acceptance is also better, because REST is currently in favor.

The Downsides of REST

There are several downsides to RESTful services, like

  • figuring out PUT vs POST (for both client and server developers),
  • making use of PATCH, and generally dealing with partial documents
  • dealing with gateways and proxies that don’t support arbitrary HTTP methods
  • running out of HTTP methods on an endpoint
  • you still have to read the docs to understand when to use which HTTP methods.  Its not self-documenting (but it is more self-documenting than RPC!)

An Experiment – converting RPC to RESTful

As an experiment, I decided to try to re-design a simple RPC API in a RESTful way.  I chose node-sharedmem, which is a Node.js HTTP server that can be used as a shared memory space for processes that have that need.

The functions on the RPC-based API are as follows:

  • save(collection, key, value, [TTL]) – saves a key-value pair in a named collection, optionally set to expire in TTL milliseconds
  • get(collection, key) – retrieves a saved value from a collection (returns the value)
  • remove(collection, key) – removes a saved key-value pair from a collection
  • increment(counter) - increments a named counter and returns the new value (integer)
  • decrement(counter) – decrements a named counter and returns the new value (integer)
  • getArray(collection) – returns all key-value pairs within a named collection

This is a very opinionated API, with no concept of a document.  I can easily identify some resources though – collections, counters and variables (key-value pairs).

I designed the following REST URIs to replace the functions, and return the same results:

  • /collection/{collection}/variable/{key} – POST, same as save function
  • /collection/{collection}/variable/{key} – GET, same as get function
  • /collection/{collection}/variable/{key} – DELETE, same as remove function
  • /counter/{counter}/increment – POST, same as increment function
  • /counter/{counter}/decrement- POST, same as decrement function
  • /collection/{collection}/variables – GET, same as getArray function

(For the counter, I decided to hard-code ‘increment’ and ‘decrement’ in the URI, rather than the more risky approach of inventing new HTTP methods).

Somewhat surprisingly, this was easy to do and the result looks RESTful to me!  (But it is not HATEOAS – meets none of the unique characteristics shown in the blue circle on the diagram).

If I had to critique the solution, I would say that

  1. it exposes too few endpoints (a more RESTful solution might expose endpoints to show all the counters, or all the collections).  This limits discoverability.
  2. the GET of a variable does not return the whole resource – it excludes the TTL and just returns the value.  So we don’t have the resource-format consistency gain that we expect from REST.

Conclusions

It is easy to put a RESTful face on an RPC web service, but the facade will not bring the full benefits of consistency (of a common resource format) and discoverability (of a complete set of resource endpoints).

That said, you do get some of the RESTful benefits – consistent endpoints, and discoverability of the current functionality.  In addition it is way less work than designing a full, discoverable RESTful API, because the full API has many more endpoints than you might want to create, test and support.

Afterthoughts on HATEOAS

I think there is very little there that is of use for APIs.  It works well for web pages, but APIs require more shared knowledge (coupling) than shared knowledge of media types can provide.

The main HATEOAS tool that many people have started to include in their APIs is the link-relations.  It is useful to have these in several scenarios, e.g.

  • paging – returning a link to the next page of results makes it much easier for both the client and the API developer
  • linking to related data – for example, a document might have links to more detail, or history, etc.

Even those links are not really HATEOAS though –  to be HATEOAS, the knowledge of how to interpret the links has to be derived from the media type of the document.  For example in HTML we know how to interpret HREFs and FORMs.  In the common formats of JSON or XML we have no such standard.

 

An experiment in re-use

The next time that you have the opportunity to re-write part or all of an existing software application, consider performing this experiment.

It is a thought experiment to help you determine what are the most valuable pieces of code that you will write, and perhaps achieve some enlightenment on the nature of software. The experiment can be done in your head or with a pen & paper:


Review the existing application, and find all of the parts that you think are re-usable in your current effort. Perhaps there will be just a few, perhaps there will be none, perhaps there will be a bunch.

Consider each of the re-usable parts and answer the questions:

  • why is this re-usable?
  • what would have to change in the current effort to make this not re-usable?

Now, skip forward in time and imagine you have successfully completed re-writing the parts that you decide to re-write. It was a wonderful success, and the application has grown and expanded in wonderful ways.

Technologies have changed, it is 10 years in the future, and it is time for the next re-write. Perform the experiment again, and write down the answers that you will want to have this time.


The point of the experiment is to highlight that there is very little value intrinsically stored in source code. The only things that have long-term value are abstractions and standards.

Standards (html, css) are valuable only as long as the technology survives. They are valuable because everyone is dependent on them, and it simply costs too much to abandon them completely.

A sub-category of standards are vendor-specific technologies. Think VB6 forms, .NET forms, ASP or PHP or XAML. They still have value, but they come with the cost of a technology tie-in – they are only valuable as long as the technology lives.

Many standards are a form of debt. You accept that you will get a lower cost of development today, but that the application will only be able to be maintained and grow as long as the technology or standard is viable. It is often a very good type of debt, because there is a balloon payment at the end that you will never have to make (because the application will die before it is necessary).

Abstractions are longer-lived, because they can represent some fundamental domain knowledge. They are valuable as long as the domain and your assumptions do not change.

 

Writing a PaaS in 1 week – retrospective

Well the week was last week, and while it was not as successful as I would have liked, it was a very productive week.

Dogfooding

http://en.wikipedia.org/wiki/Eating_your_own_dog_food.  The week was very valuable to me in that I discovered several bugs in the node-perfectapi package.  I also discovered some new features that would be helpful, so that was great because it will help me make the product better.

Design

My design had a couple of flaws.  That’s not so bad really – in my experience at least, a design seldom survives coding without some changes.  Luckily they were not major flaws –  I needed one more component to better manage the load on user-facing endpoints, and I needed to add something to support configuration of the deployed services.

Usability

Although the pieces are all working together, it is not yet a usable solution.  For one thing, there is only a publish function, no way to update or remove packages.  For another, you really need to be able to configure services for them to work.

Learning new things

This was one of the major gains for me – I learned all about Redis.  It was not pretty.  The node-redis module I used works really great, but it is at a very low level.  Coding against Redis feels a but like coding in assembly language.  Concatenating strings to form keys feels wrong, as does the process of having to create my own indexes, and do my own serializing and deserializing of objects.  There is definitely a market for modules that take the Redis experience and add a layer of abstraction in order to lower the workload.

I also discovered the async module.  Without it, coding against Redis in Node.js is next to impossible.  Really, it is ridiculously hard.  Painful like hard exercise (but without the health  benefits).

Another discovery was testing with mocha and should.  This was also a little frustrating at times, because one page (albeit a long page) of mocha documentation really does not cut it.  However once I got the hang of it, it was nice to have some tests running.

Next Steps

I’ve invested enough in this that I think I will continue to improve it until it is usable for my purposes.  Not in one week though :)

I’m also working on creating an AMI image for Nodester.  While I rejected Nodester for my own purposes (it does not have the features I wanted), I still would like to contribute a little to that project.

Writing a PaaS (using Node.js) in 1 week – Friday

This is a continuation of the post Writing a PaaS (using Node.js) in 1 week – Monday.

Well Friday is done, and it was one of those days where a stupid bug eats hours of the day.  Pushed through it though (went to lunch – my favorite technique) :)

I failed to get everything done that I wanted to in 5 days, but at the same time I have met all of my original goals, except for an Amazon AMI image for hosting the front-end proxy.

The Good

All of the components are now functioning together.  The code is split into the following:

  • Service registry – a single service which is responsible for maintaining all of the information about the installed services
  • Machine proxy – a self configuring reverse proxy running on each host machine
  • Machine host – a self configuring service host running on each host machine.  When it finds an eligible service, it installs it.  Also doubles as a command line interface to register applications (when run on a client machine)
  • Front-end proxy – a pre-configured pound reverse proxy running in front of the machines hosts, directing traffic and terminating the SSL endpoint (https).

I was able to use the CLI to register multiple apps, and the machine host and proxy services picked them up, installed them and exposed them correctly via the reverse proxy.  That is awesome!  Its just the happy path, but it makes me happy :)

I have an Amazon AMI ready for the host machine.  It is ami-114b9b78.  Login as user `ubuntu`.  There is a readme.txt in the home folder with further instructions to configure the services.  But its not much use without a server running the machine registry :(

The Bad

I realized that the frontend proxy needs to be self-configuring too, in order to direct the traffic to the correct machine(s).  So I’ll have to write another component.  The good part is that it can re-use a lot of the logic of the machine proxy code.

I also realized that I am not handling configuration for the services, which is an essential piece.  For example, if I install a service that depends on another, then I need to configure the endpoint for that other.  Perfectapi allows for service configuration through environment variables, but the service registry and the machine host do not yet have a way to make use of that technique.  I’ll have to add some code for that.

The Ugly

The code is not production ready.   It handles the happy path ok, but needs more work to be able to recover from problems (e.g. when a service fails to install).

Writing a PaaS (using Node.js) in 1 week – Thursday

This is a continuation of a previous post - Writing a PaaS (using Node.js) in 1 week – Monday.

Thursday is over, the output of which is at https://github.com/perfectapi/node-paas-machine-host.  The purpose of the machine-host is to wait around until it sees that something needs hosting, and then host it.  It also takes care of the main command-line interface, providing the ability to publish an app from the command line.

The biggest challenge yesterday was that I had originally designed the upload of a packages files as a “push”, i.e. zip up you package and publish it.  Unfortunately that causes a number of issues, the main one being that Node.js doesn’t yet have a nice unzip component.  I looked at a lot of possible packages that could handle it, but their APIs were very low level and I don’t have time to deal with that complexity.   Also, the zip files turn out to be quite big, which slows things down.  Finally, its not that friendly to make people zip it up.

So…I switched to a “pull” model, where the host will pull the files out of Git.  That limits us currently to publishing publicly accessible packages stored on github or similar host, but hat can change later.

Another challenge was something that I ave struggled with every time I try to use it – Nodes support for spawning processes.  There are 3-4 different ways to spawn a process, but the problem is that for any given scenario, only one of them works properly.  So I try `spawn`, then I debug it because it is not working, then finally I try `exec` and it works right away.  Aargh.  Not  good.

Anyway, enough writing this blog; I still have a lot to do if I want to get something working by the end of the day…

Writing a PaaS (using Node.js) in 1 week – Wednesday

This is a continuation of previous posts:

Well Wednesday is done.  The reverse proxy is written, code at https://github.com/perfectapi/node-paas-machine-proxy.

As a reminder, the purpose of the reverse proxy was to provide a single IP + host endpoint to each machine (host) on which multiple services are running.  We can run multiple services on that host, or multiple instances of the same service.  The reverse proxy provides routing and round-robin load balancing between the services.  Each service runs on a unique path, e.g. http://host/service1, http://host/service2.

A higher level proxy will provide routing between machines and SSL termination, providing endpoints like https://mydomain.com/service1.

The reverse proxy must also automatically update its configuration when it detects a change in the Service Registry, and expose load information for the host (so that we can know when it is over capacity).

How did it go?

I had some conflict on whether to use one of the node.js reverse proxies (bouncy, node-http-proxy) for the routing, or to use a more mature Linux package.  In the end I decided to go with haproxy, mainly because it has detection of when a particular endpoint is not responding, and can switch that endpoint off.  This should prove useful for when problems occur, or when services are being upgraded or scaled down.

It also supports hot-switching of the proxy configuration, which is awesome.

Once the decision was made, configuring haproxy was not very hard.  I had to take the time to read its configuration manual (a gigantic text file, argh).  Ok, I just skimmed it :)

Testing

I did less testing on this component than I did on the service registry.  I think its one of those things where you can only find the issues once you start loading up services and seeing what happens.

I did do enough testing to know that the code does what I want – its just a matter of figuring out if what I want will work in practice.  So I’ll do that on Thursday/Friday, and hopefully it will work itself out.

Determining Load

Determining the load was fairly easy.  I use a combination of the node-provided

os.loadAvg()

method, and calling

ps -A u

to find the process using the most CPU percentage.

Writing a PaaS (using Node.js) in 1 week – Tuesday

This is a continuation of the post Writing a PaaS (using Node.js) in 1 week – Monday.

Well I’m going to call Tuesday done. The Service Registry is written and installed on my server. Source code is at https://github.com/perfectapi/node-paas-registry.  As with all perfectapi APIs, there is an automatic test app page.  I’ve exposed this one to the Internet, at https://services.perfectapi.com/paas/registry/testapp/

So I’m on target, but it was a rough day.  My first challenge was Redis.  I’ve not used it before, and there was a learning curve involved before I could effectively use it.  I used the node-redis package to access it, and that package worked well.  It was just hard, trying to learn all of the commands, and when to use sets or hashes or whatever.  That ate a lot of time.

My next challenge was the async nature of the node-redis client.  Its fine if you’re just doing one thing, but I had a scenario that went like this:

  • open a connection to the database
  • get the set of instance ids by service name (via callback)
  • for each instance id, get the full instance object (via callback), and if it matches some additional criteria then do something with it and stop processing
  • close the connection to the database

The problem was that this is callback hell.  I couldn’t even figure out where to close the connection.  A little research found me the async package, which was a lifesaver.  Once I started using that, it became very natural to solve the problem.

Finally, I had some problems with perfectapi :(  Required parameters are supposed to validate, but they do not seem to be doing so.  I’ll have to take a look at that separately.

Anyway, lets hope tomorrow goes smoother…

Writing a PaaS (using Node.js) in 1 week – Monday

UPDATE:

  • The week is over.  There is a retrospective post here.
  • I have posted Tuesdays notes here, Wednesday’s notes here, Thursday’s notes here, and Friday’s notes here.

I have decided to write a simple PaaS (Platform as a Service), in order to demonstrate the capabilities of my perfectapi API framework for Node.js.   I don’t want to compete with other PaaS offerings – this is just a need that my company has – to demonstrate how to simply and easily self-host and scale services developed with perfectapi.

Doing it in one week is a challenge I put to myself, because I have a limited amount of time to devote to this, and because doing it in one week (5 days) can drive a little PR for perfectapi.

So what exactly will I be building?  The platform will have the following qualities:

  • Ability to deploy multiple Node.js perfectapi-based services, and load balance multiple instances of those services
  • Each PaaS platform supports a single domain endpoint – in my case it will be services.perfectapi.com
  • PaaS service endpoints accessible via JavaScript, Node.js, C#, REST and command line, built using perfectapi
  • Hosted service endpoints accessible via JavaScript, Node.js, C#, REST and command line, built using perfectapi
  • Provide the groundwork to automatically scale out as load increases (actual feature of automatically scaling out is not in scope for this week, but manually scaling out will be possible)
  • Services all run behind SSL
  • Complete code and Amazon AMI images (Ubuntu 11.10)  to be provided, so that others can reproduce the PaaS environment easily
  • Written in Node.js and using freely available Linux tools where necessary
  • No security to speak of (limit access using firewalls or shared secrets)
I’ll host all the code in my projects on github, and update this blog as I progress…

Day 1 (Monday) – Design

I spent most of Monday in Caribou coffee, researching and designing on my iPad.  Oh, the life I lead :)

The design I have consists of several components.  At the core is the Service Registry, which stores a list of the services installed in my domain.

Day 2 (Tuesday) - Service Registry

The service registry maintains a record of detail for each service instance, including the unique service name, and the path on which it will host.  So for example, the following:

  • “Payment Portal”, /payments, attributes,  files
  • “Payment Portal”, /payments, attributes, files
  • “Mailer”, /email, files

…represents 3 service instances.  The first two are instances of the same service, and host on the same “/payments” path.  The 3rd is another service.  Each instance has its own copy of the files, and has a set of attributes.

In my “services.perfectapi.com” domain, the records above would represent 2 endpoints:

  • https://services.perfectapi.com/payments – load balanced across 2 instances
  • https://services.perfectapi.com/email – pointing at a single instance

The Service Registry does not create these services, or manage their endpoints, or anything really other than record their existence.   It will have the following API commands and queries:

  • RegisterInstance(service name, path, attributes, files) – creates a new record of a service instance
  • UnregisterInstance(service name, path, [host], [port]) – removes a single matching existing record of a service instance
  • GetServiceInfo(service name) – returns array of host, path, port, attributes, files
  • ListServices – lists service names
  • ListUnclaimedServices – lists service names which do not yet have a port and host specified.  May list the same service name multiple times if their are multiple matches.
  • ClaimService(service name, path, host, port, attributes) – updates an unclaimed service instance with new details.  If there are multiple unclaimed instances, then only one will be updated.  If there are no matches, returns an error.
For simplicity sake, the line between a service instance and a service definition (or an “app”) is going to be blurry.

In version 1, the Service Registry will probably store its data in Redis.  In later versions I would like if it could use DNS-SD.

Day 3 (Wednesday) - Reverse Proxy

The Service Registry was a single service for a domain.  The Reverse Proxy is a single service per machine.  The Reverse Proxy monitors the Service Registry, and ensures that all instances on the current machine have a route from outside.  So for example, on the current host we may have the following in the service registry:

  • “Payment Portal”, /payments, port 4001, host ABC
  • “Payment Portal”, /payments, port 4002, host ABC
  • “Mailer”, /email, port 4003, host ABC

The Reverse Proxy ensures that all services are accessible on port 80, i.e.

  • http://abc/payments – load balanced across 2 instances
  • http://abc/email – pointing at a single instance

It also monitors the Service Registry for changes, and when it finds that there is a discrepancy, it updates the proxy configuration to create or remove a route.

In addition to routing traffic, the reverse proxy exposes an endpoint with load information for the machine it is installed on.  It exposes two methods:

  • GetLoad – returns a number indicating current 5 minute load average.  1.0 or higher indicates that the server load is at or above capacity
  • GetCulprit – returns the name of the service instance that is most likely the culprit, i.e. the instance that is using the most CPU.  (I/O ignored because Node.js processes should not be I/O intense, and network ignored because it is unlikely to be a limitation in a cloud environment).

Day 4 & 5 (Thursday & Friday) - Hosting Server

Like the Reverse Proxy, the Hosting Server runs a single instance per machine.  It contains the bulk of the PaaS functionality.  Specifically, it is responsible for:

  • installing new app instances, on request (copying files, launching the process, ensuring it remains running).
  • removing app instances, on request
  • updating App registry when apps are added or removed

The expected API methods are:

  • PublishApp – publishes a new app
  • DeleteApp – deletes an existing app
  • UpdateApp – combination of a delete and a publish (publish new, then delete old)
  • GetAppDetails – returns the attributes of an app
  • ListApps – lists all apps
  • ListAppInstances – lists the instances for an app

The Publish workflow goes something like this:

  • Write new instance(s) to app registry (with empty host and port)
  • Poll App Registry every 1 minute
  • If an unclaimed service instance is found, and this server meets to service instance requirements, and this server’s load is low enough, then claim it (write host and port to Service Registry).

The definition of “ this server’s load is low enough” is that a call to the Unix “uptime” command returns a 5 minute load average number less than 1.0 per CPU of the system.  This criteria should work on both Amazon EC2 Instances and Rackspace Cloud Servers.

Day 5 (Friday) – SSL Termination & Load Balancing

Another Reverse Proxy.  This one is a Linux package called “pound”, which can do SSL termination (host the https) as well as load balancing amongst our servers.  It works off of a static configuration file.  The file will not need to change as long as the list of servers remains static.

Auto Scaling Server (Future)

The part I will not build this week.  In order to scale out, we need to

  • detect the need to scale – using GetLoad API call on the Reverse Proxies.
  • determine which service needs scaling – using GetCulprit API call on the overloaded Reverse Proxy
  • update the Service Registry with new instances

That takes care of scaling out within the existing machines in our cloud.  Once we exhaust the capacity of the existing machines, we have to start using 3rd party services, such as SCALR or Amazon’s Auto Scaling.  In that case, it probably makes sense to move the SSL termination and Load Balancing function off of our simple “pound” package.