This is my very first time building and deploying a website so bear with me if this is a dumb question. I'm building a Heardle style clone. The idea is that every day there's a new song and people have 6 guesses to figure out which song it is from short clips of the song. Every part of this seems to work with one major exception -- I can't seem to reload today's song dynamically.
I have a function:
export async function getStaticProps() {
const allSearchableSongs = songHelper.getSongData()
const todaysSong = await songHelper.getTodaysSong()
const songURLs: string[] = (todaysSong) ? await songHelper.getTodaysSongClips(todaysSong!) : []
let valid = false
if (songURLs) {
valid = true
}
return {
props: {
allSearchableSongs,
todaysSong,
songURLs,
valid
},
revalidate: 10
};
}
Note that getTodaysSong() and getTodaysSongClips() both make calls to AWS to get data from S3. Whenever I rebuild the website this works well. However, I would like for this to refresh after 60 seconds so that nobody is ever looking at a stale website. But this doesn't change ever. The song is always out of date until I redeploy. I've checked to make sure that the data is changing daily and that's all well and good -- but the website doesn't ever reload.
I'm currently hosting this on Vercel.
What am I doing wrong? How can I ensure that this reloads after 60 seconds?
Related
I have a simple page that is applying SSR as follows:
const page = ({initProps}) => {
// render some static texts
// render images
};
page.getInitialProps = async (ctx) => {
// get id from ctx
// get data from Firestore (get by id, no aggregation)
const firebaseRes = await db.collection("organizations")
.doc(id)
.get();
// return data
}
Currently, in the production environment, it takes around 15s for TTFB.
I tried a lot of things (use next/image, reduce the data amount returned by getInitialProps...) to reduce the latency time but no luck.
Is there anything else I can check/improve for my case?
==========
Add more information:
I run my app as a Firebase function
My page is a landing page (static text, static images, dynamic image loading, one Lottie animation)
I'm using TailwindCSS
My NextJS version is 12.x
Inside the initialProp function, I connect to Firestore directly to get the data.
Inside the initialProp function, besides querying the data, I have a signInWithEmailAndPassword to get the token.
I have an app that loads some images with metadata. A single folder can be quite large (~100-142Mb) once loaded into memory. Previously, we were using a plain old javascript object to manage the state of the app and everything worked fine, but i'd like to gain the benefits of ngrx's state management.
I've discovered ngrx and it seemed to be a smarter option when it comes to state management. However, when i add these items to the state, the app hangs when adding images to the store and then performance slows down when accessing individual (and unrelated) flags from the store i.e. UI flag - draw is open.
1) Here "directories" is a Map < string, Directory > () object that is saved the the Store (~100-120Mb). Directory is a complex object with many nested values. Once images are loaded, and then added to the store, it a) hangs and then b) everything else (i.e. changing a ui flag) slows down.
return {
...state,
loadedDirectories: directories,
filesLoading: false,
};
2) The directories are then later accessed from the store.
this.store
.pipe(select(fromReducer.getLoadedDirectories))
.subscribe(loadedDirectories => {
this._directoryData = loadedDirectories;
});
Selector looks like this....
export interface ImageLoaderState {
loadedDirectories: Map<string, Directory>;
filesLoading: boolean;
errorMessage: string;
}
export class AppState {
imageLoader: fromImageLoader.ImageLoaderState;
}
export const combinedReducers = {
imageLoader: fromImageLoader.imageLoaderReducer
.... More reducers here ....
}
// Select Image loader state.
export const selectImageLoaderState = (state: AppState) => state.imageLoader;
export const getLoadedDirectories = createSelector(
selectImageLoaderState,
(state: fromImageLoader.ImageLoaderState) => state.loadedDirectories
);
Using angular 8 and the following versions of ngrx.
"#ngrx/effects": "^8.4.0",
"#ngrx/store": "^8.4.0",
"#ngrx/store-devtools": "^8.4.0",
Are there any better practices? i.e. Add each image, one at a time to the store?
The ngrx store is for application state and not so good as a document store.
Please see..
https://github.com/btroncone/ngrx-store-localstorage/issues/39
One issue I see is how you create your new state. You mention that when you create your new state, you do the following
return {
...state,
loadedDirectories: directories,
filesLoading: false,
};
I think you are creating an object with tons of key-value pairs, then recreating that work when you set the loadedDirectories property again. I'm uncertain about the performance costs of using the spread operator in the context of very large objects. I would suggest you focus on creating this property once. This might help you
Does spread operator affect performance?
I have been noticing some increase in the amount of reads in the firestore. I am testing my application on localhost.
Today I decided to have a closer look at the amount of reads and started from zero. I waited around 3 minutes without even running my application or performing any read operation, and my number of reads went up to 210, and 2 writes. Now that must be pretty weird, knowing that the application wasn't even running and it was all zero when I started.
I tried to avoid the onvalueChanges() and snapshotChanges() since they generate a lot of read.
Below is the service that I call in the home page.
Any one has an idea on what's going on, and would like to share?
Thanks in advance.
export class CatService {
lastVisibleCat: any;
cats = [];
fecha = new Date().setHours(23, 59, 5, 9);
todaysDate = new Date(this.fecha);
constructor(public afs: AngularFirestore) {
}
getCats() {
const reference = this.afs.collection('Cats');
const query1 = reference.ref.where('timeStampEndDate', '>=',
this.todaysDate);
return query1.get().then(snapShot => {
snapShot.forEach(cat => {
this.cats.push({ ...cat.data(), id: cat.id });
});
return this.cats;
});
}
}
A couple of things could be at play here:
Read counts are not real time, so the data could be coming in delayed
Reads in the Firebase Console also count towards billed operations in terms of read counts
Let's say that two users do changes to the same document while offline, but in different sections of the document. If user 2 goes back online after user 1, will the changes made by user 1 be lost?
In my database, each row contains a JS object, and one property of this object is an array. This array is bound to a series of check-boxes on the interface. What I would like is that if two users do changes to those check-boxes, the latest change is kept for each check-box individually, based on the time the when the change was made, not the time when the syncing occurred. Is GroundDB the appropriate tool to achieve this? Is there any mean to add an event handler in which I can add some logic that would be triggered when syncing occurs, and that would take care of the merging ?
The short answer is "yes" none of the ground db versions have conflict resolution since the logic is custom depending on the behaviour of conflict resolution eg. if you want to automate or involve the user.
The old Ground DB simply relied on Meteor's conflict resolution (latest data to the server wins) I'm guessing you can see some issues with that depending on the order of when which client comes online.
Ground db II doesn't have method resume it's more or less just a way to cache data offline. It's observing on an observable source.
I guess you could create a middleware observer for GDB II - one that checks the local data before doing the update and update the client or/and call the server to update the server data. This way you would have a way to handle conflicts.
I think to remember writing some code that supported "deletedAt"/"updatedAt" for some types of conflict handling, but again a conflict handler should be custom for the most part. (opening the door for reusable conflict handlers might be useful)
Especially knowing when data is removed can be tricky if you don't "soft" delete via something like using a "deletedAt" entity.
The "rc" branch is currently grounddb-caching-2016 version "2.0.0-rc.4",
I was thinking about something like:
(mind it's not tested, written directly in SO)
// Create the grounded collection
foo = new Ground.Collection('test');
// Make it observe a source (it's aware of createdAt/updatedAt and
// removedAt entities)
foo.observeSource(bar.find());
bar.find() returns a cursor with a function observe our middleware should do the same. Let's create a createMiddleWare helper for it:
function createMiddleWare(source, middleware) {
const cursor = (typeof (source||{}).observe === 'function') ? source : source.find();
return {
observe: function(observerHandle) {
const sourceObserverHandle = cursor.observe({
added: doc => {
middleware.added.call(observerHandle, doc);
},
updated: (doc, oldDoc) => {
middleware.updated.call(observerHandle, doc, oldDoc);
},
removed: doc => {
middleware.removed.call(observerHandle, doc);
},
});
// Return stop handle
return sourceObserverHandle;
}
};
}
Usage:
foo = new Ground.Collection('test');
foo.observeSource(createMiddleware(bar.find(), {
added: function(doc) {
// just pass it through
this.added(doc);
},
updated: function(doc, oldDoc) {
const fooDoc = foo.findOne(doc._id);
// Example of a simple conflict handler:
if (fooDoc && doc.updatedAt < fooDoc.updatedAt) {
// Seems like the foo doc is newer? lets update the server...
// (we'll just use the regular bar, since thats the meteor
// collection and foo is the grounded data
bar.update(doc._id, fooDoc);
} else {
// pass through
this.updated(doc, oldDoc);
}
},
removed: function(doc) {
// again just pass through for now
this.removed(doc);
}
}));
I'm learning Meteor and I was trying to pass the result of a Collection.find() into and array (using a variable) and the simpler code I have is (in a file that is in the root):
CalEvents = new Mongo.Collection('calevents'); //creating a collection
/*------------------------- Populating the database with dummy data-------*/
if (Meteor.isServer) {
Meteor.startup(function () {
if (CalEvents.find().count() === 0) {
CalEvents.insert({
title: "Initial room",
start: '2010-02-02'
});
}
});
}
/*--------------- Creating an array from the collection-----------------*/
events = [];
calEvents = CalEvents.find({});
calEvents.forEach(function(evt){
events.push({
title: evt.title,
start: evt.start,
})
});
The page has nothing to show but using the console I can see (CalEvents.find().fetch()) that I have data in my database but the "events" variable is empty...
I can't understand why because I tried several other things such as changing file names and moving code to guarantee the proper order.
And I already tried to use CalEvents.find().fetch() to create an array an put the result into a variable but I'm not able to do it...
Does anyone know what's so simple that I'm missing?...
Do you use autosubscribe?
You probably need to make sure the sbscription is ready. See Meteor: How can I tell when the database is ready? and Displaying loader while meteor collection loads.
The reason you do see CalEvents.find().fetch() returning items in the console is that by the time you make that call, the subscription is ready. But in your events = []; ... code (which I assume is in a file under the client directory, you might have assumed that the subscription data has arrived when in fact it has not.
A useful debugging tool is Chrome's device mode ("phone" icon near the search icon in DevTools), which lets you simulate slow networks (e.g. GPRS, with 500ms delay for every request).