Great question. I think Looker's multi-tenancy features are some of it's most valuable.
First, a high level recommendation. Almost any nuance between tenants can be covered easily, barring a few exceptions. Those are...
- Different (additional or subtracted) dashboard tiles per tenant. Theoretically you could use extends with LookML dashboards, but this is a bit of a clumsy solution. Best to find what tiles all your tenants will find valuable, then build single-tenant dashboards where necessary.
- Different databases per tenant (or other liquid/user attribute parameterization of the table locations). This is popular especially with SQL Server data warehouses in my experience. This will work, but since every tenant is accessing a different database, the use of PDTs will be very clumsy or impossible. Better to store all your tenants in a single database, with a column of "tenant_id" which is used in an access_filter.
- Not a limitation, but it will lead to a cap of 200-500 tenants (soft Looker limitation on model count) if you cannot handle your tenants sharing models. The tips below will help achieve this! If you ever believe you have hit a snag that will force you to split your tenants into different models, do more investigation... you're probably unaware of a feature or technique!
OK, now answers to your questions.
- The key here is access_filters. Set up a user attribute which holds the tenant's tenant_id (or similar). Then use access_filters to apply that in a WHERE clause in every query against the tenant_id column in your table(s). My method is typically to do this on a field in the "base table" (the view always in the FROM clause, usually the name of the explore), then be sure to include tenant_id in the sql_on logic for every join.
NOTE: You do NOT need a templated filter ({% condition %} xyz {% endcondition %}) in your derived tables. This will preclude the ability to persist them. Instead add tenant_id to the select clause and use it in the join.
- You will get this for free if the explores that power your tiles use access_filter. No need to even have a dashboard filter.
- Since your Looker cost is not based on activity (it is based on users in addition to platform/instances) it does not make sense to track usage in Looker. However, obviously Looker queries drive spend in your database. For this, it can be helpful to know which db queries came from which tenant (and were initiated from Looker). Looker adds its instance ID and the query ID as metadata when it sends the queries to your db, so you can join db history with Looker history and have a strong basis (including some query-specific details beyond simply a count, such as scan size) for attributing database cost. This stack overflow article does a good job explaining this. Ideally the Looker query history data extraction described there would be automated, which you could do with a scheduled delivery to a webhook and go from there.
A couple other multi-tenancy tips:
- Models should be used to control which explores users see. If your tenants have two kinds of users (maybe one detail-oriented and the other more is more casual), consider setting up multiple models and using them across all your tenants. That way you can grant some users access to simple explores, then others the complex ones. Models are the best way to control what explores (not tenant specific) your users can see. If you need to get more granular later, access_grants are available... but they are clunky to implement in any widespread manner so start with models. A good use case for access_grants is "can_see_pii" and then apply that to PII fields. A bad use case is "detail_oriented_user" which applies to hundreds of niche fields.
- It is likely you will want to have an internal and an external version of some explores. Use two models for this. Also, I fully endorse including the same view files in your internal and external models, but through experience I have stopped including/extending/refining in order to share explores between models. Just copy paste them. Trust me, new developers will be lost and make mistakes if you go this deep in pursuit of "DRY code."
- For internal users who ought to be able to see all tenants, you can set a wildcard for your tenant_id user attribute. I find this best done using groups (the internal_user group gets the wildcard value). In order to do this, the user attribute must be of the "advanced" type (e.g. string_advanced). The various wildcards are described here.